diff --git a/.clang-format b/.clang-format index 4bfce446216..8c015ef3505 100644 --- a/.clang-format +++ b/.clang-format @@ -32,16 +32,18 @@ IncludeCategories: Priority: 5 - Regex: '^(<|")Qt?[A-Z]' Priority: 6 - - Regex: '^(<|")cmsys/' + - Regex: '^ cmake.sha256sum +curl -OL "https://github.com/Kitware/CMake/releases/download/v$version/$tarball" +$shatool --check cmake.sha256sum +tar xf "$tarball" +mv "$filename" cmake + +if [ "$( uname -s )" = "Darwin" ]; then + ln -s CMake.app/Contents/bin cmake/bin +fi diff --git a/.gitlab/ci/configure_common.cmake b/.gitlab/ci/configure_common.cmake new file mode 100644 index 00000000000..df49b2d433c --- /dev/null +++ b/.gitlab/ci/configure_common.cmake @@ -0,0 +1,13 @@ +set(CTEST_USE_LAUNCHERS "ON" CACHE BOOL "") + +# We run the install right after the build. Avoid rerunning it when installing. +set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY "ON" CACHE BOOL "") +# Install CMake under the build tree. +set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "") +set(CMake_TEST_INSTALL "OFF" CACHE BOOL "") + +if (NOT "$ENV{CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE "$ENV{CMAKE_BUILD_TYPE}" CACHE STRING "") +endif () + +include("${CMAKE_CURRENT_LIST_DIR}/configure_sccache.cmake") diff --git a/.gitlab/ci/configure_cuda10.2_nvidia.cmake b/.gitlab/ci/configure_cuda10.2_nvidia.cmake new file mode 100644 index 00000000000..519699b0ccf --- /dev/null +++ b/.gitlab/ci/configure_cuda10.2_nvidia.cmake @@ -0,0 +1,3 @@ +set(CMake_TEST_CUDA "NVIDIA" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_debian10_iwyu.cmake b/.gitlab/ci/configure_debian10_iwyu.cmake new file mode 100644 index 00000000000..1daa581b365 --- /dev/null +++ b/.gitlab/ci/configure_debian10_iwyu.cmake @@ -0,0 +1,4 @@ +set(CMake_RUN_IWYU ON CACHE BOOL "") +set(IWYU_COMMAND "/usr/bin/include-what-you-use-6.0" CACHE FILEPATH "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/configure_external_test.cmake b/.gitlab/ci/configure_external_test.cmake new file mode 100644 index 00000000000..71397d18901 --- /dev/null +++ b/.gitlab/ci/configure_external_test.cmake @@ -0,0 +1,3 @@ +set(CMake_TEST_HOST_CMAKE "ON" CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/configure_fedora31_common.cmake b/.gitlab/ci/configure_fedora31_common.cmake new file mode 100644 index 00000000000..dc068d51643 --- /dev/null +++ b/.gitlab/ci/configure_fedora31_common.cmake @@ -0,0 +1,4 @@ +set(BUILD_CursesDialog ON CACHE BOOL "") +set(BUILD_QtDialog ON CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/configure_fedora31_makefiles.cmake b/.gitlab/ci/configure_fedora31_makefiles.cmake new file mode 100644 index 00000000000..20863a2fb08 --- /dev/null +++ b/.gitlab/ci/configure_fedora31_makefiles.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_fedora31_ninja.cmake b/.gitlab/ci/configure_fedora31_ninja.cmake new file mode 100644 index 00000000000..74768b7001a --- /dev/null +++ b/.gitlab/ci/configure_fedora31_ninja.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora31_common.cmake") diff --git a/.gitlab/ci/configure_fedora31_ninja_multi.cmake b/.gitlab/ci/configure_fedora31_ninja_multi.cmake new file mode 100644 index 00000000000..20863a2fb08 --- /dev/null +++ b/.gitlab/ci/configure_fedora31_ninja_multi.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_fedora31_sphinx.cmake b/.gitlab/ci/configure_fedora31_sphinx.cmake new file mode 100644 index 00000000000..dfc9b8cf491 --- /dev/null +++ b/.gitlab/ci/configure_fedora31_sphinx.cmake @@ -0,0 +1,8 @@ +set(SPHINX_INFO ON CACHE BOOL "") +set(SPHINX_MAN ON CACHE BOOL "") +set(SPHINX_HTML ON CACHE BOOL "") +set(SPHINX_SINGLEHTML ON CACHE BOOL "") +set(SPHINX_QTHELP ON CACHE BOOL "") +set(SPHINX_TEXT ON CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/configure_fedora31_tidy.cmake b/.gitlab/ci/configure_fedora31_tidy.cmake new file mode 100644 index 00000000000..55d022c8996 --- /dev/null +++ b/.gitlab/ci/configure_fedora31_tidy.cmake @@ -0,0 +1,3 @@ +set(CMake_RUN_CLANG_TIDY ON CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora31_common.cmake") diff --git a/.gitlab/ci/configure_macos_common.cmake b/.gitlab/ci/configure_macos_common.cmake new file mode 100644 index 00000000000..bd5902b08bf --- /dev/null +++ b/.gitlab/ci/configure_macos_common.cmake @@ -0,0 +1,9 @@ +# Our CI machines do not consistently have Java installed, so a build may +# detect that Java is available and working, but a test machine then not have a +# working Java installed. To work around this, just act as if Java is not +# available on any CI machine. +set(Java_JAVA_EXECUTABLE "" CACHE FILEPATH "") +set(Java_JAVAC_EXECUTABLE "" CACHE FILEPATH "") +set(Java_JAR_EXECUTABLE "" CACHE FILEPATH "") + +set(BUILD_QtDialog ON CACHE BOOL "") diff --git a/.gitlab/ci/configure_macos_makefiles.cmake b/.gitlab/ci/configure_macos_makefiles.cmake new file mode 100644 index 00000000000..85f67b53149 --- /dev/null +++ b/.gitlab/ci/configure_macos_makefiles.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/configure_macos_ninja.cmake b/.gitlab/ci/configure_macos_ninja.cmake new file mode 100644 index 00000000000..85f67b53149 --- /dev/null +++ b/.gitlab/ci/configure_macos_ninja.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/configure_macos_xcode.cmake b/.gitlab/ci/configure_macos_xcode.cmake new file mode 100644 index 00000000000..1b976d261ae --- /dev/null +++ b/.gitlab/ci/configure_macos_xcode.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_sccache.cmake b/.gitlab/ci/configure_sccache.cmake new file mode 100644 index 00000000000..261bb285f97 --- /dev/null +++ b/.gitlab/ci/configure_sccache.cmake @@ -0,0 +1,2 @@ +set(CMAKE_C_COMPILER_LAUNCHER "sccache" CACHE STRING "") +set(CMAKE_CXX_COMPILER_LAUNCHER "sccache" CACHE STRING "") diff --git a/.gitlab/ci/configure_windows_common.cmake b/.gitlab/ci/configure_windows_common.cmake new file mode 100644 index 00000000000..45250acbe20 --- /dev/null +++ b/.gitlab/ci/configure_windows_common.cmake @@ -0,0 +1,4 @@ +set(BUILD_QtDialog ON CACHE BOOL "") +set(CMAKE_PREFIX_PATH "$ENV{CI_PROJECT_DIR}/.gitlab/qt" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/configure_windows_vs2019_x64.cmake b/.gitlab/ci/configure_windows_vs2019_x64.cmake new file mode 100644 index 00000000000..f6ece57c884 --- /dev/null +++ b/.gitlab/ci/configure_windows_vs2019_x64.cmake @@ -0,0 +1,3 @@ +set(CMake_TEST_WIX_NO_VERIFY "ON" CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_windows_vs2019_x64_ninja.cmake b/.gitlab/ci/configure_windows_vs2019_x64_ninja.cmake new file mode 100644 index 00000000000..719c93c1a83 --- /dev/null +++ b/.gitlab/ci/configure_windows_vs2019_x64_ninja.cmake @@ -0,0 +1,3 @@ +set(CMake_TEST_WIX_NO_VERIFY "ON" CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_common.cmake") diff --git a/.gitlab/ci/ctest_build.cmake b/.gitlab/ci/ctest_build.cmake new file mode 100644 index 00000000000..6402a5d2061 --- /dev/null +++ b/.gitlab/ci/ctest_build.cmake @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.8) + +include("${CMAKE_CURRENT_LIST_DIR}/gitlab_ci.cmake") + +# Read the files from the build directory. +ctest_read_custom_files("${CTEST_BINARY_DIRECTORY}") + +# Pick up from where the configure left off. +ctest_start(APPEND) + +if (CTEST_CMAKE_GENERATOR STREQUAL "Unix Makefiles") + include(ProcessorCount) + ProcessorCount(nproc) + set(CTEST_BUILD_FLAGS "-j${nproc}") +endif () + +ctest_build( + NUMBER_WARNINGS num_warnings + RETURN_VALUE build_result) +ctest_submit(PARTS Build) + +if (build_result) + message(FATAL_ERROR + "Failed to build") +endif () + +if ("$ENV{CTEST_NO_WARNINGS_ALLOWED}" AND num_warnings GREATER 0) + message(FATAL_ERROR + "Found ${num_warnings} warnings (treating as fatal).") +endif () + +if (NOT "$ENV{CMake_SKIP_INSTALL}") + ctest_build(APPEND + TARGET install + RETURN_VALUE install_result) + + if (install_result) + message(FATAL_ERROR + "Failed to install") + endif () +endif () diff --git a/.gitlab/ci/ctest_configure.cmake b/.gitlab/ci/ctest_configure.cmake new file mode 100644 index 00000000000..55cad135a07 --- /dev/null +++ b/.gitlab/ci/ctest_configure.cmake @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.8) + +include("${CMAKE_CURRENT_LIST_DIR}/gitlab_ci.cmake") + +set(cmake_args + -C "${CMAKE_CURRENT_LIST_DIR}/configure_$ENV{CMAKE_CONFIGURATION}.cmake") + +# Create an entry in CDash. +ctest_start(Experimental TRACK "${ctest_track}") + +# Gather update information. +find_package(Git) +set(CTEST_UPDATE_VERSION_ONLY ON) +set(CTEST_UPDATE_COMMAND "${GIT_EXECUTABLE}") +ctest_update() + +# Configure the project. +ctest_configure( + OPTIONS "${cmake_args}" + RETURN_VALUE configure_result) + +# Read the files from the build directory. +ctest_read_custom_files("${CTEST_BINARY_DIRECTORY}") + +# We can now submit because we've configured. This is a cmb-superbuild-ism. +ctest_submit(PARTS Update) +ctest_submit(PARTS Configure) + +if (configure_result) + message(FATAL_ERROR + "Failed to configure") +endif () diff --git a/.gitlab/ci/ctest_exclusions.cmake b/.gitlab/ci/ctest_exclusions.cmake new file mode 100644 index 00000000000..b885a6a5e78 --- /dev/null +++ b/.gitlab/ci/ctest_exclusions.cmake @@ -0,0 +1,19 @@ +set(test_exclusions + # This test hits global resources and can be handled by nightly testing. + # https://gitlab.kitware.com/cmake/cmake/-/merge_requests/4769 + "^BundleGeneratorTest$" +) + +if (CTEST_CMAKE_GENERATOR MATCHES "Visual Studio") + list(APPEND test_exclusions + # This test takes around 5 minutes with Visual Studio. + # https://gitlab.kitware.com/cmake/cmake/-/issues/20733 + "^ExternalProjectUpdate$" + # This test is a dependency of the above and is only required for it. + "^ExternalProjectUpdateSetup$") +endif () + +string(REPLACE ";" "|" test_exclusions "${test_exclusions}") +if (test_exclusions) + set(test_exclusions "(${test_exclusions})") +endif () diff --git a/.gitlab/ci/ctest_test.cmake b/.gitlab/ci/ctest_test.cmake new file mode 100644 index 00000000000..569139d2834 --- /dev/null +++ b/.gitlab/ci/ctest_test.cmake @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.8) + +include("${CMAKE_CURRENT_LIST_DIR}/gitlab_ci.cmake") + +# Read the files from the build directory. +ctest_read_custom_files("${CTEST_BINARY_DIRECTORY}") + +# Pick up from where the configure left off. +ctest_start(APPEND) + +include(ProcessorCount) +ProcessorCount(nproc) + +include("${CMAKE_CURRENT_LIST_DIR}/ctest_exclusions.cmake") +ctest_test( + PARALLEL_LEVEL "${nproc}" + RETURN_VALUE test_result + EXCLUDE "${test_exclusions}") +ctest_submit(PARTS Test) + +if (test_result) + message(FATAL_ERROR + "Failed to test") +endif () diff --git a/.gitlab/ci/ctest_test_external.cmake b/.gitlab/ci/ctest_test_external.cmake new file mode 100644 index 00000000000..d92b936d268 --- /dev/null +++ b/.gitlab/ci/ctest_test_external.cmake @@ -0,0 +1,73 @@ +cmake_minimum_required(VERSION 3.8) + +include("${CMAKE_CURRENT_LIST_DIR}/gitlab_ci.cmake") + +set(cmake_args + -C "${CMAKE_CURRENT_LIST_DIR}/configure_$ENV{CMAKE_CONFIGURATION}.cmake") + +# Create an entry in CDash. +ctest_start(Experimental TRACK "${ctest_track}") + +# Gather update information. +find_package(Git) +set(CTEST_UPDATE_VERSION_ONLY ON) +set(CTEST_UPDATE_COMMAND "${GIT_EXECUTABLE}") +ctest_update() + +# Configure the project. +ctest_configure( + OPTIONS "${cmake_args}" + RETURN_VALUE configure_result) + +# Read the files from the build directory. +ctest_read_custom_files("${CTEST_BINARY_DIRECTORY}") + +# We can now submit because we've configured. This is a cmb-superbuild-ism. +ctest_submit(PARTS Update) +ctest_submit(PARTS Configure) + +if (configure_result) + message(FATAL_ERROR + "Failed to configure") +endif () + +include(ProcessorCount) +ProcessorCount(nproc) + +if (CTEST_CMAKE_GENERATOR STREQUAL "Unix Makefiles") + set(CTEST_BUILD_FLAGS "-j${nproc}") +endif () + +ctest_build( + NUMBER_WARNINGS num_warnings + RETURN_VALUE build_result) +ctest_submit(PARTS Build) + +if (build_result) + message(FATAL_ERROR + "Failed to build") +endif () + +if ("$ENV{CTEST_NO_WARNINGS_ALLOWED}" AND num_warnings GREATER 0) + message(FATAL_ERROR + "Found ${num_warnings} warnings (treating as fatal).") +endif () + +set(ctest_label_args) +if (NOT "$ENV{CTEST_LABELS}" STREQUAL "") + list(APPEND ctest_label_args + INCLUDE_LABEL "$ENV{CTEST_LABELS}") +endif () + +include("${CMAKE_CURRENT_LIST_DIR}/ctest_exclusions.cmake") +ctest_test( + PARALLEL_LEVEL "${nproc}" + RETURN_VALUE test_result + ${ctest_label_args} + EXCLUDE "${test_exclusions}") +ctest_submit(PARTS Test) + +if (test_result) + message(FATAL_ERROR + "Failed to test") +endif () diff --git a/.gitlab/ci/docker/cuda10.2/Dockerfile b/.gitlab/ci/docker/cuda10.2/Dockerfile new file mode 100644 index 00000000000..e0ea0e73623 --- /dev/null +++ b/.gitlab/ci/docker/cuda10.2/Dockerfile @@ -0,0 +1,5 @@ +FROM nvidia/cuda:10.2-devel-ubuntu18.04 +MAINTAINER Ben Boeckel + +COPY install_deps.sh /root/install_deps.sh +RUN sh /root/install_deps.sh diff --git a/.gitlab/ci/docker/cuda10.2/install_deps.sh b/.gitlab/ci/docker/cuda10.2/install_deps.sh new file mode 100755 index 00000000000..0d57cd386d8 --- /dev/null +++ b/.gitlab/ci/docker/cuda10.2/install_deps.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e + +apt-get update + +# Install development tools. +apt-get install -y \ + g++ \ + curl \ + git + +apt-get clean diff --git a/.gitlab/ci/docker/debian10/Dockerfile b/.gitlab/ci/docker/debian10/Dockerfile new file mode 100644 index 00000000000..e8c38512cd1 --- /dev/null +++ b/.gitlab/ci/docker/debian10/Dockerfile @@ -0,0 +1,15 @@ +FROM debian:10 as iwyu-build +MAINTAINER Ben Boeckel + +COPY install_iwyu.sh /root/install_iwyu.sh +RUN sh /root/install_iwyu.sh + +FROM debian:10 +MAINTAINER Ben Boeckel + +COPY install_deps.sh /root/install_deps.sh +RUN sh /root/install_deps.sh + +COPY --from=iwyu-build /root/iwyu.tar.gz /root/iwyu.tar.gz +RUN tar -C / -xf /root/iwyu.tar.gz +RUN ln -s /usr/lib/llvm-6.0/bin/include-what-you-use /usr/bin/include-what-you-use-6.0 diff --git a/.gitlab/ci/docker/debian10/install_deps.sh b/.gitlab/ci/docker/debian10/install_deps.sh new file mode 100755 index 00000000000..9c32d64adc8 --- /dev/null +++ b/.gitlab/ci/docker/debian10/install_deps.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e + +apt-get update + +# Install build requirements. +apt-get install -y \ + libssl-dev + +# Install development tools. +apt-get install -y \ + g++ \ + curl \ + git + +# Install iwyu runtime deps. +apt-get install -y \ + clang-6.0 \ + libncurses6 + +apt-get clean diff --git a/.gitlab/ci/docker/debian10/install_iwyu.sh b/.gitlab/ci/docker/debian10/install_iwyu.sh new file mode 100755 index 00000000000..54d26efd4d6 --- /dev/null +++ b/.gitlab/ci/docker/debian10/install_iwyu.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +set -e + +# Install development tools. +apt-get update +apt-get install -y \ + clang-6.0 \ + libclang-6.0-dev \ + llvm-6.0-dev \ + libz-dev \ + g++ \ + cmake \ + ninja-build \ + git + +cd /root +git clone "https://github.com/include-what-you-use/include-what-you-use.git" +cd include-what-you-use +readonly llvm_version="$( clang-6.0 --version | head -n1 | cut -d' ' -f3 | cut -d. -f-2 )" +git checkout "clang_$llvm_version" +mkdir build +cd build + +cmake -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + "-DCMAKE_INSTALL_PREFIX=/usr/lib/llvm-$llvm_version" \ + "-DIWYU_LLVM_ROOT_PATH=/usr/lib/llvm-$llvm_version" \ + .. +ninja +DESTDIR=/root/iwyu-destdir ninja install +tar -C /root/iwyu-destdir -cf /root/iwyu.tar.gz . diff --git a/.gitlab/ci/docker/fedora31/Dockerfile b/.gitlab/ci/docker/fedora31/Dockerfile new file mode 100644 index 00000000000..5588a85e3bf --- /dev/null +++ b/.gitlab/ci/docker/fedora31/Dockerfile @@ -0,0 +1,5 @@ +FROM fedora:31 +MAINTAINER Ben Boeckel + +COPY install_deps.sh /root/install_deps.sh +RUN sh /root/install_deps.sh diff --git a/.gitlab/ci/docker/fedora31/install_deps.sh b/.gitlab/ci/docker/fedora31/install_deps.sh new file mode 100755 index 00000000000..0d857c1a6bb --- /dev/null +++ b/.gitlab/ci/docker/fedora31/install_deps.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Install build requirements. +dnf install --setopt=install_weak_deps=False -y \ + ncurses-devel \ + openssl-devel \ + qt5-qtbase-devel + +# Install development tools. +dnf install --setopt=install_weak_deps=False -y \ + clang-tools-extra \ + gcc-c++ \ + git-core \ + make + +# Install documentation tools. +dnf install --setopt=install_weak_deps=False -y \ + python3-sphinx \ + texinfo \ + qt5-qttools-devel + +# Tools needed for the test suite. +dnf install --setopt=install_weak_deps=False -y \ + findutils \ + file + +dnf clean all diff --git a/.gitlab/ci/download_qt.cmake b/.gitlab/ci/download_qt.cmake new file mode 100644 index 00000000000..c392b1c4a2c --- /dev/null +++ b/.gitlab/ci/download_qt.cmake @@ -0,0 +1,146 @@ +cmake_minimum_required(VERSION 3.12) + +# Input variables. +set(qt_version_major "5") +set(qt_version_minor "15") +set(qt_version_patch "0") +# This URL is only visible inside of Kitware's network. Please use your own Qt +# Account to obtain these files. +set(qt_url_root "https://paraview.org/files/dependencies/internal/qt") + +# Determine the ABI to fetch for Qt. +if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "vs2015") + set(qt_platform "windows_x86") + set(msvc_year "2015") + set(qt_abi "win64_msvc${msvc_year}_64") +elseif ("$ENV{CMAKE_CONFIGURATION}" MATCHES "vs2017" OR + "$ENV{CMAKE_CONFIGURATION}" MATCHES "vs2019") + set(qt_platform "windows_x86") + set(msvc_year "2017") + set(qt_abi "win64_msvc${msvc_year}_64") +elseif ("$ENV{CMAKE_CONFIGURATION}" MATCHES "macos") + set(qt_platform "mac_x64") + set(qt_abi "clang_64") +else () + message(FATAL_ERROR + "Unknown ABI to use for Qt") +endif () + +# Combined version variables. +set(qt_version "${qt_version_major}.${qt_version_minor}.${qt_version_patch}") +set(qt_version_nodot "${qt_version_major}${qt_version_minor}${qt_version_patch}") + +# Files needed to download. +set(qt_files) +if (qt_platform STREQUAL "windows_x86") + if (msvc_year STREQUAL "2017") + set(qt_build_stamp "202002260536") + elseif (msvc_year STREQUAL "2015") + set(qt_build_stamp "202005150700") + else () + message(FATAL_ERROR + "Build stamp for MSVC ${msvc_year} is unknown") + endif () + + set(qt_file_name_prefix "${qt_version}-0-${qt_build_stamp}") + + foreach (qt_component IN ITEMS qtbase qtwinextras) + list(APPEND qt_files + "${qt_file_name_prefix}${qt_component}-Windows-Windows_10-MSVC${msvc_year}-Windows-Windows_10-X86_64.7z") + endforeach () + + set(qt_subdir "${qt_version}/msvc${msvc_year}_64") +elseif (qt_platform STREQUAL "mac_x64") + set(qt_build_stamp "202005140805") + set(qt_file_name_prefix "${qt_version}-0-${qt_build_stamp}") + + foreach (qt_component IN ITEMS qtbase) + list(APPEND qt_files + "${qt_file_name_prefix}${qt_component}-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64.7z") + endforeach () + + set(qt_subdir "${qt_version}/clang_64") +else () + message(FATAL_ERROR + "Unknown files for ${qt_platform}") +endif () + +# Verify that we know what directory will be extracted. +if (NOT qt_subdir) + message(FATAL_ERROR + "The extracted subdirectory is not set") +endif () + +# Build up the path to the file to download. +set(qt_url_path "${qt_platform}/desktop/qt5_${qt_version_nodot}/qt.qt5.${qt_version_nodot}.${qt_abi}") +set(qt_url_prefix "${qt_url_root}/${qt_url_path}") + +# Include the file containing the hashes of the files that matter. +include("${CMAKE_CURRENT_LIST_DIR}/download_qt_hashes.cmake") + +# Download and extract each file. +foreach (qt_file IN LISTS qt_files) + # Ensure we have a hash to verify. + if (NOT DEFINED "${qt_file}_hash") + message(FATAL_ERROR + "Unknown hash for ${qt_file}") + endif () + + # Download the file. + file(DOWNLOAD + "${qt_url_prefix}/${qt_file}" + ".gitlab/${qt_file}" + STATUS download_status + EXPECTED_HASH "SHA256=${${qt_file}_hash}") + + # Check the download status. + list(GET download_status 0 res) + if (res) + list(GET download_status 1 err) + message(FATAL_ERROR + "Failed to download ${qt_file}: ${err}") + endif () + + # Extract the file. + execute_process( + COMMAND + "${CMAKE_COMMAND}" + -E tar + xf "${qt_file}" + WORKING_DIRECTORY ".gitlab" + RESULT_VARIABLE res + ERROR_VARIABLE err + ERROR_STRIP_TRAILING_WHITESPACE) + if (res) + message(FATAL_ERROR + "Failed to extract ${qt_file}: ${err}") + endif () +endforeach () + +# The Windows tarballs have some unfortunate permissions in them that prevent +# deletion when `git clean -ffdx` tries to clean up the directory. +if (qt_platform STREQUAL "windows_x86") + # Fix permissions. + file(TO_NATIVE_PATH ".gitlab/${qt_subdir}/*.*" native_qt_dir) + execute_process( + # Remove any read-only flags that aren't affected by `icacls`. + COMMAND + attrib + -r # Remove readonly flag + "${native_qt_dir}" + /d # Treat as a directory + /s # Recursive + /l # Don't dereference symlinks + RESULT_VARIABLE res + ERROR_VARIABLE err + ERROR_STRIP_TRAILING_WHITESPACE) + if (res) + message(FATAL_ERROR + "Failed to fix remove read-only flags in ${qt_file}: ${err}") + endif () +endif () + +# Move to a predictable prefix. +file(RENAME + ".gitlab/${qt_subdir}" + ".gitlab/qt") diff --git a/.gitlab/ci/download_qt_hashes.cmake b/.gitlab/ci/download_qt_hashes.cmake new file mode 100644 index 00000000000..59cb5977790 --- /dev/null +++ b/.gitlab/ci/download_qt_hashes.cmake @@ -0,0 +1,11 @@ +# Lines can be generated by doing: +# +# sha256sum $files | awk '{ print "set(\"" $2 "_hash\" " $1 ")" }' >> $thisfile + +set("5.15.0-0-202002260536qtbase-Windows-Windows_10-MSVC2017-Windows-Windows_10-X86_64.7z_hash" c041596be8f7a16c7be9ea6757c14766ff3200ab6d56f7db8f865dbfe039fe20) +set("5.15.0-0-202002260536qtwinextras-Windows-Windows_10-MSVC2017-Windows-Windows_10-X86_64.7z_hash" 10796128fac54f146767e33f6872975ba238858547de7a9650ec4cd9581fe71a) + +set("5.15.0-0-202005150700qtbase-Windows-Windows_10-MSVC2015-Windows-Windows_10-X86_64.7z_hash" 0c2653c5eca256f59c0b48b537cd633b05560e4241c0226856d2ae22ab066df4) +set("5.15.0-0-202005150700qtwinextras-Windows-Windows_10-MSVC2015-Windows-Windows_10-X86_64.7z_hash" 4bca3a8d8c7611e211a82d86b3396f8a622abe7859d5052452414642ec191844) + +set("5.15.0-0-202005140805qtbase-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64.7z_hash" 04d867c81d2431f288c42c9752642759460b9468477de349368dcc8de0c8ddc4) diff --git a/.gitlab/ci/gitlab_ci.cmake b/.gitlab/ci/gitlab_ci.cmake new file mode 100644 index 00000000000..698d5ea7f14 --- /dev/null +++ b/.gitlab/ci/gitlab_ci.cmake @@ -0,0 +1,58 @@ +if (NOT DEFINED "ENV{GITLAB_CI}") + message(FATAL_ERROR + "This script assumes it is being run inside of GitLab-CI") +endif () + +# Set up the source and build paths. +set(CTEST_SOURCE_DIRECTORY "$ENV{CI_PROJECT_DIR}") +if (NOT "$ENV{CTEST_SOURCE_SUBDIRECTORY}" STREQUAL "") + string(APPEND CTEST_SOURCE_DIRECTORY "/$ENV{CTEST_SOURCE_SUBDIRECTORY}") +endif () +set(CTEST_BINARY_DIRECTORY "${CTEST_SOURCE_DIRECTORY}/build") + +if ("$ENV{CMAKE_CONFIGURATION}" STREQUAL "") + message(FATAL_ERROR + "The CMAKE_CONFIGURATION environment variable is required to know what " + "cache initialization file to use.") +endif () + +# Set the build metadata. +set(CTEST_BUILD_NAME "$ENV{CI_PROJECT_NAME}-$ENV{CMAKE_CONFIGURATION}") +set(CTEST_SITE "gitlab-ci") + +# Default to Release builds. +if (NOT "$ENV{CMAKE_BUILD_TYPE}" STREQUAL "") + set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_BUILD_TYPE}") +endif () +if (NOT CTEST_BUILD_CONFIGURATION) + set(CTEST_BUILD_CONFIGURATION "Release") +endif () +set(CTEST_CONFIGURATION_TYPE "${CTEST_BUILD_CONFIGURATION}") + +# Default to using Ninja. +if (NOT "$ENV{CMAKE_GENERATOR}" STREQUAL "") + set(CTEST_CMAKE_GENERATOR "$ENV{CMAKE_GENERATOR}") +endif () +if (NOT CTEST_CMAKE_GENERATOR) + set(CTEST_CMAKE_GENERATOR "Ninja") +endif () + +# Set the toolset and platform if requested. +if (NOT "$ENV{CMAKE_GENERATOR_PLATFORM}" STREQUAL "") + set(CTEST_CMAKE_GENERATOR_PLATFORM "$ENV{CMAKE_GENERATOR_PLATFORM}") +endif () +if (NOT "$ENV{CMAKE_GENERATOR_TOOLSET}" STREQUAL "") + set(CTEST_CMAKE_GENERATOR_TOOLSET "$ENV{CMAKE_GENERATOR_TOOLSET}") +endif () + +# Determine the track to submit to. +set(ctest_track "Experimental") +if (NOT "$ENV{CI_MERGE_REQUEST_ID}" STREQUAL "") + set(ctest_track "merge-requests") +elseif ("$ENV{CI_PROJECT_PATH}" STREQUAL "cmake/cmake") + if ("$ENV{CI_COMMIT_REF_NAME}" STREQUAL "master") + set(ctest_track "master") + elseif ("$ENV{CI_COMMIT_REF_NAME}" STREQUAL "release") + set(ctest_track "release") + endif () +endif () diff --git a/.gitlab/ci/ninja.ps1 b/.gitlab/ci/ninja.ps1 new file mode 100755 index 00000000000..91f8b02b74b --- /dev/null +++ b/.gitlab/ci/ninja.ps1 @@ -0,0 +1,17 @@ +$erroractionpreference = "stop" + +$version = "1.10.0" +$sha256sum = "919FD158C16BF135E8A850BB4046EC1CE28A7439EE08B977CD0B7F6B3463D178" +$filename = "ninja-win" +$tarball = "$filename.zip" + +$outdir = $pwd.Path +$outdir = "$outdir\.gitlab" +Invoke-WebRequest -Uri "https://github.com/ninja-build/ninja/releases/download/v$version/$tarball" -OutFile "$outdir\$tarball" +$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256 +if ($hash.Hash -ne $sha256sum) { + exit 1 +} + +Add-Type -AssemblyName System.IO.Compression.FileSystem +[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir") diff --git a/.gitlab/ci/ninja.sh b/.gitlab/ci/ninja.sh new file mode 100755 index 00000000000..93c1ee95732 --- /dev/null +++ b/.gitlab/ci/ninja.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +set -e + +readonly version="1.10.0" + +case "$( uname -s )" in + Linux) + shatool="sha256sum" + sha256sum="6566836ddf3d72ca06685b34814e0c6fa0f0943542d651d0dab3150f10307c82" + platform="linux" + ;; + Darwin) + shatool="shasum -a 256" + sha256sum="2ee405c0e205d55666c60cc9c0d8d04c8ede06d3ef2e2c2aabe08fd81c17d22e" + platform="mac" + ;; + *) + echo "Unrecognized platform $( uname -s )" + exit 1 + ;; +esac +readonly shatool +readonly sha256sum +readonly platform + +readonly filename="ninja-$platform" +readonly tarball="$filename.zip" + +cd .gitlab + +echo "$sha256sum $tarball" > ninja.sha256sum +curl -OL "https://github.com/ninja-build/ninja/releases/download/v$version/$tarball" +$shatool --check ninja.sha256sum +./cmake/bin/cmake -E tar xf "$tarball" diff --git a/.gitlab/ci/sccache.sh b/.gitlab/ci/sccache.sh new file mode 100755 index 00000000000..f7d51ffe471 --- /dev/null +++ b/.gitlab/ci/sccache.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +set -e + +readonly version="0.2.13" + +case "$( uname -s )" in + Linux) + shatool="sha256sum" + sha256sum="28a5499e340865b08b632306b435913beb590fbd7b49a3f887a623b459fabdeb" + platform="x86_64-unknown-linux-musl" + ;; + Darwin) + shatool="shasum -a 256" + sha256sum="f564e948abadfc9e409eb1cd7babf24c6784057d5506c3b0a04cdd37cd830ae9" + platform="x86_64-apple-darwin" + ;; + *) + echo "Unrecognized platform $( uname -s )" + exit 1 + ;; +esac +readonly shatool +readonly sha256sum +readonly platform + +readonly filename="sccache-$version-$platform" +readonly tarball="$filename.tar.gz" + +cd .gitlab + +echo "$sha256sum $tarball" > sccache.sha256sum +curl -OL "https://github.com/mozilla/sccache/releases/download/$version/$tarball" +$shatool --check sccache.sha256sum +tar xf "$tarball" +mv "$filename/sccache" . diff --git a/.gitlab/ci/vcvarsall.ps1 b/.gitlab/ci/vcvarsall.ps1 new file mode 100755 index 00000000000..57d338632bf --- /dev/null +++ b/.gitlab/ci/vcvarsall.ps1 @@ -0,0 +1,9 @@ +$erroractionpreference = "stop" + +cmd /c "`"$env:VCVARSALL`" $VCVARSPLATFORM -vcvars_ver=$VCVARSVERSION & set" | +foreach { + if ($_ -match "=") { + $v = $_.split("=") + [Environment]::SetEnvironmentVariable($v[0], $v[1]) + } +} diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml new file mode 100644 index 00000000000..1c9737734d9 --- /dev/null +++ b/.gitlab/os-linux.yml @@ -0,0 +1,210 @@ +# Linux-specific builder configurations and build commands + +## Base images + +### Release + +.release_linux: + image: "kitware/cmake:build-linux-x86_64-deps-2020-04-02@sha256:77e9ab183f34680990db9da5945473e288f0d6556bce79ecc1589670d656e157" + + variables: + GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" + LAUNCHER: "scl enable devtoolset-6 rh-python36 --" + +### Debian + +.debian10: + image: "kitware/cmake:ci-debian10-x86_64-2020-04-27" + + variables: + GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" + +.debian10_iwyu: + extends: .debian10 + + variables: + CMAKE_CONFIGURATION: debian10_iwyu + CTEST_NO_WARNINGS_ALLOWED: 1 + CMake_SKIP_INSTALL: 1 + +### Fedora + +.fedora31: + image: "kitware/cmake:ci-fedora31-x86_64-2020-06-01" + + variables: + GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" + +#### Lint builds + +.fedora31_tidy: + extends: .fedora31 + + variables: + CMAKE_CONFIGURATION: fedora31_tidy + CTEST_NO_WARNINGS_ALLOWED: 1 + CMake_SKIP_INSTALL: 1 + +.fedora31_sphinx: + extends: .fedora31 + + variables: + CMAKE_CONFIGURATION: fedora31_sphinx + CTEST_NO_WARNINGS_ALLOWED: 1 + CTEST_SOURCE_SUBDIRECTORY: "Utilities/Sphinx" + CMake_SKIP_INSTALL: 1 + +#### Build and test + +.fedora31_ninja: + extends: .fedora31 + + variables: + CMAKE_CONFIGURATION: fedora31_ninja + CTEST_NO_WARNINGS_ALLOWED: 1 + +.fedora31_ninja_multi: + extends: .fedora31 + + variables: + CMAKE_CONFIGURATION: fedora31_ninja_multi + CTEST_NO_WARNINGS_ALLOWED: 1 + CMAKE_GENERATOR: "Ninja Multi-Config" + +.fedora31_makefiles: + extends: .fedora31 + + variables: + CMAKE_CONFIGURATION: fedora31_makefiles + CTEST_NO_WARNINGS_ALLOWED: 1 + CMAKE_GENERATOR: "Unix Makefiles" + +### CUDA builds + +.cuda10.2: + image: "kitware/cmake:ci-cuda10.2-x86_64-2020-06-11" + + variables: + GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" + CTEST_LABELS: "CUDA" + +.cuda10.2_nvidia: + extends: .cuda10.2 + + variables: + CMAKE_CONFIGURATION: cuda10.2_nvidia + CTEST_NO_WARNINGS_ALLOWED: 1 + +## Tags + +.linux_builder_tags: + tags: + - build + - docker + - linux + +.linux_builder_tags_qt: + tags: + - build + - docker + - linux + - linux-3.17 # Needed to be able to load Fedora's Qt libraries. + +.linux_builder_tags_cuda: + tags: + - cuda-rt + - docker + - linux + +## Linux-specific scripts + +.before_script_linux: &before_script_linux + - .gitlab/ci/cmake.sh + - .gitlab/ci/ninja.sh + - export PATH=$PWD/.gitlab:$PWD/.gitlab/cmake/bin:$PATH + - cmake --version + - ninja --version + +.cmake_build_linux: + stage: build + + script: + - *before_script_linux + - .gitlab/ci/sccache.sh + - sccache --start-server + - sccache --show-stats + - "$LAUNCHER ctest -VV -S .gitlab/ci/ctest_configure.cmake" + - "$LAUNCHER ctest -VV -S .gitlab/ci/ctest_build.cmake" + - sccache --show-stats + + interruptible: true + +.cmake_test_linux: + stage: test + + script: + - *before_script_linux + - "$LAUNCHER ctest --output-on-failure -V -S .gitlab/ci/ctest_test.cmake" + + interruptible: true + +.cmake_build_release_linux: + stage: build + + script: + # Bootstrap. + - mkdir build/ + - cp -v Utilities/Release/linux/x86_64/cache.txt build/CMakeCache.txt + # Make sccache available. + - .gitlab/ci/sccache.sh + - export PATH=$PWD/.gitlab:$PATH + # Append sccache settings to the cache. + - echo "CMAKE_C_COMPILER_LAUNCHER:STRING=sccache" >> build/CMakeCache.txt + - echo "CMAKE_CXX_COMPILER_LAUNCHER:STRING=sccache" >> build/CMakeCache.txt + # CI settings. + - echo "CMake_TEST_INSTALL:BOOL=OFF" >> build/CMakeCache.txt + - echo "CMAKE_INSTALL_PREFIX:PATH=$PWD/build/install" >> build/CMakeCache.txt + - echo "CMAKE_SKIP_INSTALL_ALL_DEPENDENCY:BOOL=ON" >> build/CMakeCache.txt + # Bootstrap + - cd build/ + - "$LAUNCHER ../bootstrap --parallel=$(nproc) --docdir=doc/cmake" + # FIXME: When CTest can drive an external CMake for the build as well, + # use the scripts here. + - "$LAUNCHER make -j$(nproc)" + # NOTE: This regex matches that used in the release build. + - "$LAUNCHER bin/ctest --output-on-failure -j$(nproc) -R '^(CMake\\.|CMakeLib\\.|CMakeServerLib\\.|RunCMake\\.ctest_memcheck)'" + # Make a package. + - bin/cpack -G TGZ + - bin/cpack -G STGZ + - sccache --show-stats + + interruptible: true + +.cmake_test_linux_package: + stage: test-ext + + script: + - *before_script_linux + # Make the CMake package available. + - mkdir -p build/install + - tar -C build/install --strip-components=1 -xzf build/cmake-*-Linux-x86_64.tar.gz + - .gitlab/ci/sccache.sh + - sccache --start-server + - sccache --show-stats + - "$LAUNCHER build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_test_external.cmake" + - sccache --show-stats + + interruptible: true + +.cmake_test_linux_external: + stage: test-ext + + script: + - *before_script_linux + - .gitlab/ci/sccache.sh + - sccache --start-server + - sccache --show-stats + - "$LAUNCHER build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_test_external.cmake" + - sccache --show-stats + + interruptible: true diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml new file mode 100644 index 00000000000..9d123ecab2c --- /dev/null +++ b/.gitlab/os-macos.yml @@ -0,0 +1,115 @@ +# macOS-specific builder configurations and build commands + +## Base configurations + +.macos: + variables: + GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci ext/$CI_CONCURRENT_ID" + # TODO: Factor this out so that each job selects the Xcode version to + # use so that different versions can be tested in a single pipeline. + DEVELOPER_DIR: "/Applications/Xcode-11.5.app/Contents/Developer" + +### Build and test + +.macos_build: + extends: .macos + + variables: + # Note that shell runners only support runners with a single + # concurrency level. We can't use `$CI_CONCURRENCY_ID` because this may + # change between the build and test stages which CMake doesn't support. + # Even if we could, it could change if other runners on the machine + # could run at the same time, so we drop it. + GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" + +.macos_ninja: + extends: .macos_build + + variables: + CMAKE_CONFIGURATION: macos_ninja + CTEST_NO_WARNINGS_ALLOWED: 1 + +.macos_makefiles: + extends: .macos_build + + variables: + CMAKE_CONFIGURATION: macos_makefiles + CTEST_NO_WARNINGS_ALLOWED: 1 + CMAKE_GENERATOR: "Unix Makefiles" + +### External testing + +.macos_xcode: + extends: .macos + + variables: + CMAKE_CONFIGURATION: macos_xcode + CMAKE_GENERATOR: Xcode + +## Tags + +.macos_builder_tags: + tags: + - cmake # Since this is a bare runner, pin to a project. + - macos + - shell + - xcode-11.5 + - nonconcurrent + +.macos_builder_ext_tags: + tags: + - cmake # Since this is a bare runner, pin to a project. + - macos + - shell + - xcode-11.5 + - concurrent + +## macOS-specific scripts + +.before_script_macos: &before_script_macos + - .gitlab/ci/cmake.sh + - .gitlab/ci/ninja.sh + - export PATH=$PWD/.gitlab:$PWD/.gitlab/cmake/bin:$PATH + - cmake --version + - ninja --version + # Download Qt + - cmake -P .gitlab/ci/download_qt.cmake + - export CMAKE_PREFIX_PATH=$PWD/.gitlab/qt + +.cmake_build_macos: + stage: build + + script: + - *before_script_macos + - .gitlab/ci/sccache.sh + # Allow the server to already be running. + - "sccache --start-server || :" + - sccache --show-stats + - ctest -VV -S .gitlab/ci/ctest_configure.cmake + - ctest -VV -S .gitlab/ci/ctest_build.cmake + - sccache --show-stats + + interruptible: true + +.cmake_test_macos: + stage: test + + script: + - *before_script_macos + - ctest --output-on-failure -V -S .gitlab/ci/ctest_test.cmake + + interruptible: true + +.cmake_test_macos_external: + stage: test-ext + + script: + - *before_script_macos + - .gitlab/ci/sccache.sh + # Allow the server to already be running. + - "sccache --start-server || :" + - sccache --show-stats + - "$LAUNCHER build/install/CMake.app/Contents/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_test_external.cmake" + - sccache --show-stats + + interruptible: true diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml new file mode 100644 index 00000000000..910a232c035 --- /dev/null +++ b/.gitlab/os-windows.yml @@ -0,0 +1,116 @@ +# Windows-specific builder configurations and build commands + +## Base configurations + +.windows: + variables: + GIT_CLONE_PATH: "$CI_BUILDS_DIR\\cmake ci ext\\$CI_CONCURRENT_ID" + +### Build and test + +.windows_build: + extends: .windows + + variables: + # Note that shell runners only support runners with a single + # concurrency level. We can't use `$CI_CONCURRENCY_ID` because this may + # change between the build and test stages which CMake doesn't support. + # Even if we could, it could change if other runners on the machine + # could run at the same time, so we drop it. + GIT_CLONE_PATH: "$CI_BUILDS_DIR\\cmake ci" + +.windows_ninja: + extends: .windows_build + + variables: + # Debug and RelWithDebinfo build types use the `/Zi` which results in + # uncacheable compiations. + # https://github.com/mozilla/sccache/issues/242 + CMAKE_BUILD_TYPE: Release + CTEST_NO_WARNINGS_ALLOWED: 1 + +.windows_vs2019_x64_ninja: + extends: .windows_ninja + + variables: + CMAKE_CONFIGURATION: windows_vs2019_x64_ninja + VCVARSALL: "${VS160COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat" + VCVARSPLATFORM: "x64" + VCVARSVERSION: "14.26" + +### External testing + +.windows_vs2019_x64: + extends: .windows + + variables: + CMAKE_CONFIGURATION: windows_vs2019_x64 + CMAKE_GENERATOR: "Visual Studio 16 2019" + CMAKE_GENERATOR_PLATFORM: "x64" + CMAKE_GENERATOR_TOOLSET: "v142,version=14.26" + +## Tags + +.windows_builder_tags: + tags: + - cmake # Since this is a bare runner, pin to a project. + - windows + - shell + - vs2019 + - msvc-19.25 + - nonconcurrent + +.windows_builder_ext_tags: + tags: + - cmake # Since this is a bare runner, pin to a project. + - windows + - shell + - vs2019 + - msvc-19.25 + - concurrent + +## Windows-specific scripts + +.before_script_windows: &before_script_windows + - Invoke-Expression -Command .gitlab/ci/cmake.ps1 + - Invoke-Expression -Command .gitlab/ci/ninja.ps1 + - $pwdpath = $pwd.Path + - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab;$pwdpath\.gitlab\cmake\bin;$env:PATH" + - cmake --version + - ninja --version + - cmake -P .gitlab/ci/download_qt.cmake + - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\qt\bin;$env:PATH" + +.cmake_build_windows: + stage: build + + script: + - *before_script_windows + - Set-Item -Force -Path "env:PATH" -Value "$env:PATH;$env:SCCACHE_PATH" + - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 + - sccache --start-server + - sccache --show-stats + - ctest -VV -S .gitlab/ci/ctest_configure.cmake + - ctest -VV -S .gitlab/ci/ctest_build.cmake + - sccache --show-stats + - sccache --stop-server + + interruptible: true + +.cmake_test_windows: + stage: test + + script: + - *before_script_windows + - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 + - ctest --output-on-failure -V -S .gitlab/ci/ctest_test.cmake + + interruptible: true + +.cmake_test_windows_external: + stage: test-ext + + script: + - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_test_external.cmake + + interruptible: true diff --git a/.gitlab/rules.yml b/.gitlab/rules.yml new file mode 100644 index 00000000000..88756eb6ab5 --- /dev/null +++ b/.gitlab/rules.yml @@ -0,0 +1,18 @@ +# Rules for where jobs can run + +.run_manually: + rules: + - if: '$CI_PROJECT_PATH == "cmake/cmake"' + when: delayed + start_in: 5 minutes + - if: '$CI_MERGE_REQUEST_ID' + when: manual + - when: never + +.run_automatically: + rules: + - if: '$CI_PROJECT_PATH == "cmake/cmake"' + when: on_success + - if: '$CI_MERGE_REQUEST_ID' + when: on_success + - when: never diff --git a/Auxiliary/CMakeLists.txt b/Auxiliary/CMakeLists.txt index 53cf2c518dc..c0aebef30aa 100644 --- a/Auxiliary/CMakeLists.txt +++ b/Auxiliary/CMakeLists.txt @@ -1,4 +1,16 @@ -install(DIRECTORY vim/indent vim/syntax DESTINATION ${CMAKE_DATA_DIR}/editors/vim) -install(FILES cmake-mode.el DESTINATION ${CMAKE_DATA_DIR}/editors/emacs) +# Install Vim files to a typical system integration directory. +# Packagers can set CMake_INSTALL_VIMFILES_DIR to control this. +if(NOT CMake_INSTALL_VIMFILES_DIR) + set(CMake_INSTALL_VIMFILES_DIR ${CMAKE_XDGDATA_DIR}/vim/vimfiles) +endif() +install(DIRECTORY vim/indent vim/syntax DESTINATION ${CMake_INSTALL_VIMFILES_DIR}) + +# Install Emacs files to a typical system integration directory. +# Packagers can set CMake_INSTALL_EMACS_DIR to control this. +if(NOT CMake_INSTALL_EMACS_DIR) + set(CMake_INSTALL_EMACS_DIR ${CMAKE_XDGDATA_DIR}/emacs/site-lisp) +endif() +install(FILES cmake-mode.el DESTINATION ${CMake_INSTALL_EMACS_DIR}) + install(FILES cmake.m4 DESTINATION ${CMAKE_XDGDATA_DIR}/aclocal) add_subdirectory (bash-completion) diff --git a/Auxiliary/bash-completion/CMakeLists.txt b/Auxiliary/bash-completion/CMakeLists.txt index c0a88999ecd..93b6ffd0e3c 100644 --- a/Auxiliary/bash-completion/CMakeLists.txt +++ b/Auxiliary/bash-completion/CMakeLists.txt @@ -1,8 +1,21 @@ -# Always install completion file in local dir -# in order to be sure to always be able to install -# in a local user directory rooted in a single directory. -# packager should either patch that out or -# add symlinks to the files in appropriate places -# /etc/bash_completion.d/ -# DATADIR/completions (may be /usr/share//completions -install(FILES cmake cpack ctest DESTINATION ${CMAKE_DATA_DIR}/completions) +# We need to integrate into the system install, or this will silently fail to +# accomplish anything at all, and packagers won't even know it exists. Use the +# `/bash-completion/completions/` hierarchy by default, rooted in +# CMake's XDGDATA_DIR definition of the sharedir. This works with installation +# to `/usr` or `/usr/local` (or any prefix which bash-completion is configured +# with) as well as a simple installation by a local user into their home +# directory *if* the prefix is `$HOME/.local` since `.local/share/` is part of +# the bash-completion search path too. +# For more complex installations, packagers can set CMake_INSTALL_BASH_COMP_DIR +# to another system location. + +if(NOT CMake_INSTALL_BASH_COMP_DIR) + if(CMAKE_BASH_COMP_DIR) + # Honor previous customization option. + set(CMake_INSTALL_BASH_COMP_DIR "${CMAKE_BASH_COMP_DIR}") + else() + # Default. + set(CMake_INSTALL_BASH_COMP_DIR ${CMAKE_XDGDATA_DIR}/bash-completion/completions) + endif() +endif() +install(FILES cmake cpack ctest DESTINATION ${CMake_INSTALL_BASH_COMP_DIR}) diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index 0676f7ed296..2d63eb0d546 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim @@ -170,6 +170,7 @@ syn keyword cmakeProperty contained \ FRAMEWORK_VERSION \ Fortran_FORMAT \ Fortran_MODULE_DIRECTORY + \ Fortran_PREPROCESS \ GENERATED \ GENERATOR_FILE_NAME \ GENERATOR_IS_MULTI_CONFIG @@ -1019,6 +1020,7 @@ syn keyword cmakeVariable contained \ CMAKE_Fortran_MODULE_DIRECTORY \ CMAKE_Fortran_OUTPUT_EXTENSION \ CMAKE_Fortran_PLATFORM_ID + \ CMAKE_Fortran_PREPROCESS \ CMAKE_Fortran_SIMULATE_ID \ CMAKE_Fortran_SIMULATE_VERSION \ CMAKE_Fortran_SIZEOF_DATA_PTR diff --git a/CMakeLists.txt b/CMakeLists.txt index 18b8d7d6e33..f62c666bd16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE) if(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") message(FATAL_ERROR "CMake no longer compiles on HP-UX. See\n" - " https://gitlab.kitware.com/cmake/cmake/issues/17137\n" + " https://gitlab.kitware.com/cmake/cmake/-/issues/17137\n" "Use CMake 3.9 or lower instead." ) endif() @@ -154,7 +154,7 @@ macro(CMAKE_HANDLE_SYSTEM_LIBRARIES) # Allow the user to enable/disable all system utility library options by # defining CMAKE_USE_SYSTEM_LIBRARIES or CMAKE_USE_SYSTEM_LIBRARY_${util}. - set(UTILITIES BZIP2 CURL EXPAT FORM JSONCPP LIBARCHIVE LIBLZMA LIBRHASH LIBUV ZLIB ZSTD) + set(UTILITIES BZIP2 CURL EXPAT FORM JSONCPP LIBARCHIVE LIBLZMA LIBRHASH LIBUV NGHTTP2 ZLIB ZSTD) foreach(util ${UTILITIES}) if(NOT DEFINED CMAKE_USE_SYSTEM_LIBRARY_${util} AND DEFINED CMAKE_USE_SYSTEM_LIBRARIES) @@ -192,6 +192,8 @@ macro(CMAKE_HANDLE_SYSTEM_LIBRARIES) "${CMAKE_USE_SYSTEM_LIBRARY_ZSTD}" "NOT CMAKE_USE_SYSTEM_LIBARCHIVE" ON) CMAKE_DEPENDENT_OPTION(CMAKE_USE_SYSTEM_LIBLZMA "Use system-installed liblzma" "${CMAKE_USE_SYSTEM_LIBRARY_LIBLZMA}" "NOT CMAKE_USE_SYSTEM_LIBARCHIVE" ON) + CMAKE_DEPENDENT_OPTION(CMAKE_USE_SYSTEM_NGHTTP2 "Use system-installed nghttp2" + "${CMAKE_USE_SYSTEM_LIBRARY_NGHTTP2}" "NOT CMAKE_USE_SYSTEM_CURL" ON) option(CMAKE_USE_SYSTEM_FORM "Use system-installed libform" "${CMAKE_USE_SYSTEM_LIBRARY_FORM}") option(CMAKE_USE_SYSTEM_JSONCPP "Use system-installed jsoncpp" "${CMAKE_USE_SYSTEM_LIBRARY_JSONCPP}") option(CMAKE_USE_SYSTEM_LIBRHASH "Use system-installed librhash" "${CMAKE_USE_SYSTEM_LIBRARY_LIBRHASH}") @@ -335,6 +337,9 @@ endmacro() macro (CMAKE_BUILD_UTILITIES) find_package(Threads) + # Suppress unnecessary checks in third-party code. + include(Utilities/cmThirdPartyChecks.cmake) + #--------------------------------------------------------------------- # Create the kwsys library for CMake. set(KWSYS_NAMESPACE cmsys) @@ -351,6 +356,21 @@ macro (CMAKE_BUILD_UTILITIES) if(CMake_NO_CXX_STANDARD) set(KWSYS_CXX_STANDARD "") endif() + if(WIN32) + # FIXME: Teach KWSys to hard-code these checks on Windows. + set(KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC_COMPILED 0) + set(KWSYS_C_HAS_PTRDIFF_T_COMPILED 1) + set(KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H_COMPILED 1) + set(KWSYS_CXX_HAS_RLIMIT64_COMPILED 0) + set(KWSYS_CXX_HAS_SETENV_COMPILED 0) + set(KWSYS_CXX_HAS_UNSETENV_COMPILED 0) + set(KWSYS_CXX_HAS_UTIMENSAT_COMPILED 0) + set(KWSYS_CXX_HAS_UTIMES_COMPILED 0) + set(KWSYS_CXX_STAT_HAS_ST_MTIM_COMPILED 0) + set(KWSYS_CXX_STAT_HAS_ST_MTIMESPEC_COMPILED 0) + set(KWSYS_STL_HAS_WSTRING_COMPILED 1) + set(KWSYS_SYS_HAS_IFADDRS_H 0) + endif() add_subdirectory(Source/kwsys) set(kwsys_folder "Utilities/KWSys") CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE} "${kwsys_folder}") @@ -374,10 +394,13 @@ macro (CMAKE_BUILD_UTILITIES) include_directories(SYSTEM "${CMake_SOURCE_DIR}/Utilities/std") endif() - include_directories( - ${CMake_BINARY_DIR}/Utilities - ${CMake_SOURCE_DIR}/Utilities - ) + include_directories("${CMake_BINARY_DIR}/Utilities") + if ((CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # using -isystem option generate error "template with C linkage" + include_directories("${CMake_SOURCE_DIR}/Utilities") + else() + include_directories(SYSTEM "${CMake_SOURCE_DIR}/Utilities") + endif() #--------------------------------------------------------------------- # Build CMake std library for CMake and CTest. @@ -430,6 +453,13 @@ macro (CMAKE_BUILD_UTILITIES) else() set(CMAKE_ZLIB_INCLUDES ${CMake_SOURCE_DIR}/Utilities) set(CMAKE_ZLIB_LIBRARIES cmzlib) + set(WITHOUT_ZLIB_DLL "") + set(WITHOUT_ZLIB_DLL_WITH_LIB cmzlib) + set(ZLIB_DLL "") + set(ZLIB_DLL_WITH_LIB cmzlib) + set(ZLIB_WINAPI "") + set(ZLIB_WINAPI_COMPILED 0) + set(ZLIB_WINAPI_WITH_LIB cmzlib) add_subdirectory(Utilities/cmzlib) CMAKE_SET_TARGET_FOLDER(cmzlib "Utilities/3rdParty") endif() @@ -465,9 +495,19 @@ macro (CMAKE_BUILD_UTILITIES) set(CURL_CA_PATH "" CACHE PATH "Path to SSL CA Certificate Directory") mark_as_advanced(CURL_CA_BUNDLE CURL_CA_PATH) endif() + if(NOT CMAKE_USE_SYSTEM_NGHTTP2) + # Tell curl's FindNGHTTP2 module to use our library. + set(NGHTTP2_LIBRARY cmnghttp2) + set(NGHTTP2_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmnghttp2/lib/includes) + endif() add_subdirectory(Utilities/cmcurl) CMAKE_SET_TARGET_FOLDER(cmcurl "Utilities/3rdParty") CMAKE_SET_TARGET_FOLDER(LIBCURL "Utilities/3rdParty") + if(NOT CMAKE_USE_SYSTEM_NGHTTP2) + # Configure after curl to re-use some check results. + add_subdirectory(Utilities/cmnghttp2) + CMAKE_SET_TARGET_FOLDER(cmnghttp2 "Utilities/3rdParty") + endif() endif() #--------------------------------------------------------------------- @@ -496,6 +536,11 @@ macro (CMAKE_BUILD_UTILITIES) set(BZIP2_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmbzip2") set(BZIP2_LIBRARIES cmbzip2) + set(BZIP2_NEED_PREFIX "") + set(USE_BZIP2_DLL "") + set(USE_BZIP2_DLL_WITH_LIB cmbzip2) + set(USE_BZIP2_STATIC "") + set(USE_BZIP2_STATIC_WITH_LIB cmbzip2) add_subdirectory(Utilities/cmbzip2) CMAKE_SET_TARGET_FOLDER(cmbzip2 "Utilities/3rdParty") endif() @@ -547,21 +592,25 @@ macro (CMAKE_BUILD_UTILITIES) set(ZLIB_INCLUDE_DIR ${CMAKE_ZLIB_INCLUDES}) set(ZLIB_LIBRARY ${CMAKE_ZLIB_LIBRARIES}) add_definitions(-DLIBARCHIVE_STATIC) + set(ENABLE_MBEDTLS OFF CACHE INTERNAL "Enable use of mbed TLS") set(ENABLE_NETTLE OFF CACHE INTERNAL "Enable use of Nettle") set(ENABLE_OPENSSL ${CMAKE_USE_OPENSSL} CACHE INTERNAL "Enable use of OpenSSL") + set(ENABLE_LIBB2 OFF CACHE INTERNAL "Enable the use of the system LIBB2 library if found") set(ENABLE_LZMA ON CACHE INTERNAL "Enable the use of the system LZMA library if found") set(ENABLE_LZ4 OFF CACHE INTERNAL "Enable the use of the system LZ4 library if found") set(ENABLE_LZO OFF CACHE INTERNAL "Enable the use of the system LZO library if found") set(ENABLE_ZLIB ON CACHE INTERNAL "Enable the use of the system ZLIB library if found") set(ENABLE_BZip2 ON CACHE INTERNAL "Enable the use of the system BZip2 library if found") + set(ENABLE_ZSTD ON CACHE INTERNAL "Enable the use of the system zstd library if found") set(ENABLE_LIBXML2 OFF CACHE INTERNAL "Enable the use of the system libxml2 library if found") - set(ENABLE_EXPAT ON CACHE INTERNAL "Enable the use of the system EXPAT library if found") + set(ENABLE_EXPAT OFF CACHE INTERNAL "Enable the use of the system EXPAT library if found") set(ENABLE_PCREPOSIX OFF CACHE INTERNAL "Enable the use of the system PCREPOSIX library if found") set(ENABLE_LibGCC OFF CACHE INTERNAL "Enable the use of the system LibGCC library if found") set(ENABLE_XATTR OFF CACHE INTERNAL "Enable extended attribute support") set(ENABLE_ACL OFF CACHE INTERNAL "Enable ACL support") set(ENABLE_ICONV OFF CACHE INTERNAL "Enable iconv support") set(ENABLE_CNG OFF CACHE INTERNAL "Enable the use of CNG(Crypto Next Generation)") + SET(POSIX_REGEX_LIB "" CACHE INTERNAL "Choose what library should provide POSIX regular expression support") add_subdirectory(Utilities/cmlibarchive) CMAKE_SET_TARGET_FOLDER(cmlibarchive "Utilities/3rdParty") set(CMAKE_TAR_LIBRARIES cmlibarchive ${BZIP2_LIBRARIES}) @@ -677,10 +726,15 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE) include(${CMake_SOURCE_DIR}/Tests/CMakeInstall.cmake) endif() - # no clue why we are testing for this here - include(CheckSymbolExists) - CHECK_SYMBOL_EXISTS(unsetenv "stdlib.h" HAVE_UNSETENV) - CHECK_SYMBOL_EXISTS(environ "stdlib.h" HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE) + # Checks for cmSystemTools. + if(WIN32) + set(HAVE_UNSETENV 0) + set(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE 1) + else() + include(CheckSymbolExists) + CHECK_SYMBOL_EXISTS(unsetenv "stdlib.h" HAVE_UNSETENV) + CHECK_SYMBOL_EXISTS(environ "stdlib.h" HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE) + endif() endif() # CMAKE_TESTS_CDASH_SERVER: CDash server used by CMake/Tests. @@ -810,6 +864,10 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE) PATTERN "*.sh*" PERMISSIONS OWNER_READ OWNER_EXECUTE OWNER_WRITE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + REGEX "/(ExportImportList|cpp)$" + PERMISSIONS OWNER_READ OWNER_EXECUTE OWNER_WRITE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE REGEX "Help/(dev|guide)($|/)" EXCLUDE ) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 7983be16ba8..300621405cb 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -11,11 +11,11 @@ Community CMake is maintained and supported by `Kitware`_ and developed in collaboration with a productive community of contributors. -Please subscribe and post to the `CMake Developers List`_ to raise +Please post to the ``Development`` category of the `CMake Forum`_ to raise discussion of development topics. .. _`Kitware`: http://www.kitware.com/cmake -.. _`CMake Developers List`: https://cmake.org/mailman/listinfo/cmake-developers +.. _`CMake Forum`: https://discourse.cmake.org Patches ======= diff --git a/CTestConfig.cmake b/CTestConfig.cmake index 020582e4576..9ec9e8f7252 100644 --- a/CTestConfig.cmake +++ b/CTestConfig.cmake @@ -1,6 +1,8 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. +# If changing this file, also update Utilities/Sphinx/CTestConfig.cmake + set(CTEST_PROJECT_NAME "CMake") set(CTEST_NIGHTLY_START_TIME "1:00:00 UTC") diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in index fb8e099a6f0..4c8267de8c2 100644 --- a/CTestCustom.cmake.in +++ b/CTestCustom.cmake.in @@ -11,6 +11,7 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "warning: \\(Long double usage is reported only once for each file" "warning: To disable this warning use" "could not be inlined" + "libcmexpat.*has no symbols" "libcmcurl.*has no symbols" "not sorted slower link editing will result" "stl_deque.h:479" @@ -64,6 +65,7 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "ld: warning .*/libgcc.a archive's cputype" "ld: warning: ignoring file .*/libgcc.a, file was built for archive which is not the architecture being linked" "ld: warning: in .*/libgcc.a, file is not of required architecture" + "ld: warning: symbol .(deflate|inflate)_copyright. has differing sizes" # system libz and QtCore disagree "warning.*This version of Mac OS X is unsupported" "clang.*: warning: argument unused during compilation: .-g" "note: in expansion of macro" # diagnostic context note @@ -80,7 +82,7 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "compilation completed with warnings" # PGI "[0-9]+ Warning\\(s\\) detected" # SunPro -# scanbuild exceptions + # scanbuild exceptions "char_traits.h:.*: warning: Null pointer argument in call to string length function" "stl_construct.h:.*: warning: Forming reference to null pointer" ".*stl_uninitialized.h:75:19: warning: Forming reference to null pointer.*" @@ -99,6 +101,9 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "liblzma/common/index_encoder.c:[0-9]+:[0-9]+: warning: Value stored to .* during its initialization is never read" "libuv/src/.*:[0-9]+:[0-9]+: warning: Dereference of null pointer" "libuv/src/.*:[0-9]+:[0-9]+: warning: The left operand of '==' is a garbage value" + "libuv/src/.*:[0-9]+:[0-9]+: warning: 1st function call argument is an uninitialized value" + "nghttp2/lib/.*:[0-9]+:[0-9]+: warning: Dereference of null pointer" + "nghttp2/lib/.*:[0-9]+:[0-9]+: warning: Value stored to .* is never read" ) if(NOT "@CMAKE_GENERATOR@" MATCHES "Xcode") diff --git a/Help/command/DEVICE_LINK_OPTIONS.txt b/Help/command/DEVICE_LINK_OPTIONS.txt new file mode 100644 index 00000000000..3f0226fdcd2 --- /dev/null +++ b/Help/command/DEVICE_LINK_OPTIONS.txt @@ -0,0 +1,11 @@ + +When a device link step is involved, which is controlled by +:prop_tgt:`CUDA_SEPARABLE_COMPILATION` and +:prop_tgt:`CUDA_RESOLVE_DEVICE_SYMBOLS` properties and policy :policy:`CMP0105`, +the raw options will be delivered to the host and device link steps (wrapped in +``-Xcompiler`` or equivalent for device link). Options wrapped with +``$`` +:manual:`generator expression ` will be used +only for the device link step. Options wrapped with ``$`` +:manual:`generator expression ` will be used +only for the host link step. diff --git a/Help/command/FIND_XXX.txt b/Help/command/FIND_XXX.txt index 42bf52b0860..4a62c5b5baa 100644 --- a/Help/command/FIND_XXX.txt +++ b/Help/command/FIND_XXX.txt @@ -15,6 +15,7 @@ The general signature is: [PATHS path1 [path2 ... ENV var]] [PATH_SUFFIXES suffix1 [suffix2 ...]] [DOC "cache documentation string"] + [REQUIRED] [NO_DEFAULT_PATH] [NO_PACKAGE_ROOT_PATH] [NO_CMAKE_PATH] @@ -31,8 +32,9 @@ A cache entry named by ```` is created to store the result of this command. If the |SEARCH_XXX| is found the result is stored in the variable and the search will not be repeated unless the variable is cleared. -If nothing is found, the result will be -``-NOTFOUND``, and the search will be attempted again the +If nothing is found, the result will be ``-NOTFOUND``. +The ``REQUIRED`` option stops processing with an error message if nothing +is found, otherwise the search will be attempted again the next time |FIND_XXX| is invoked with the same variable. Options include: @@ -57,6 +59,9 @@ Options include: ``DOC`` Specify the documentation string for the ```` cache entry. +``REQUIRED`` + Stop processing with an error message if nothing is found. + If ``NO_DEFAULT_PATH`` is specified, then no additional paths are added to the search. If ``NO_DEFAULT_PATH`` is not specified, the search process is as follows: @@ -138,6 +143,10 @@ If ``NO_DEFAULT_PATH`` is not specified, the search process is as follows: * |CMAKE_SYSTEM_XXX_PATH| * |CMAKE_SYSTEM_XXX_MAC_PATH| + The platform paths that these variables contain are locations that + typically include installed software. An example being ``/usr/local`` for + UNIX based platforms. + 7. Search the paths specified by the PATHS option or in the short-hand version of the command. These are typically hard-coded guesses. diff --git a/Help/command/add_compile_definitions.rst b/Help/command/add_compile_definitions.rst index 8225da783e4..e10aba022ef 100644 --- a/Help/command/add_compile_definitions.rst +++ b/Help/command/add_compile_definitions.rst @@ -7,10 +7,12 @@ Add preprocessor definitions to the compilation of source files. add_compile_definitions( ...) -Adds preprocessor definitions to the compiler command line for targets in the -current directory and below (whether added before or after this command is -invoked). See documentation of the :prop_dir:`directory ` -and :prop_tgt:`target ` ``COMPILE_DEFINITIONS`` properties. +Adds preprocessor definitions to the compiler command line. + +The preprocessor definitions are added to the :prop_dir:`COMPILE_DEFINITIONS` +directory property for the current ``CMakeLists`` file. They are also added to +the :prop_tgt:`COMPILE_DEFINITIONS` target property for each target in the +current ``CMakeLists`` file. Definitions are specified using the syntax ``VAR`` or ``VAR=value``. Function-style definitions are not supported. CMake will automatically diff --git a/Help/command/add_compile_options.rst b/Help/command/add_compile_options.rst index 43805c3a91b..36f403c8aee 100644 --- a/Help/command/add_compile_options.rst +++ b/Help/command/add_compile_options.rst @@ -46,3 +46,6 @@ to use the more specific commands :command:`add_compile_definitions` and :command:`include_directories`. The command :command:`target_compile_options` adds target-specific options. + +The source file property :prop_sf:`COMPILE_OPTIONS` adds options to one +source file. diff --git a/Help/command/add_definitions.rst b/Help/command/add_definitions.rst index d06b01c9a41..fe691883639 100644 --- a/Help/command/add_definitions.rst +++ b/Help/command/add_definitions.rst @@ -8,9 +8,9 @@ Add -D define flags to the compilation of source files. add_definitions(-DFOO -DBAR ...) Adds definitions to the compiler command line for targets in the current -directory and below (whether added before or after this command is invoked). -This command can be used to add any flags, but it is intended to add -preprocessor definitions. +directory, whether added before or after this command is invoked, and for +the ones in sub-directories added after. This command can be used to add any +flags, but it is intended to add preprocessor definitions. .. note:: diff --git a/Help/command/add_executable.rst b/Help/command/add_executable.rst index 0a7d7e15102..e073228de6a 100644 --- a/Help/command/add_executable.rst +++ b/Help/command/add_executable.rst @@ -1,8 +1,15 @@ add_executable -------------- +.. only:: html + + .. contents:: + Add an executable to the project using the specified source files. +Normal Executables +^^^^^^^^^^^^^^^^^^ + .. code-block:: cmake add_executable( [WIN32] [MACOSX_BUNDLE] @@ -45,7 +52,8 @@ See also :prop_sf:`HEADER_FILE_ONLY` on what to do if some sources are pre-processed, and you want to have the original sources reachable from within IDE. --------------------------------------------------------------------------- +Imported Executables +^^^^^^^^^^^^^^^^^^^^ .. code-block:: cmake @@ -65,7 +73,8 @@ whose names begin in ``IMPORTED_``. The most important such property is the main executable file on disk. See documentation of the ``IMPORTED_*`` properties for more information. --------------------------------------------------------------------------- +Alias Executables +^^^^^^^^^^^^^^^^^ .. code-block:: cmake @@ -74,8 +83,13 @@ properties for more information. Creates an :ref:`Alias Target `, such that ```` can be used to refer to ```` in subsequent commands. The ```` does not appear in the generated buildsystem as a make target. The -```` may not be a non-``GLOBAL`` -:ref:`Imported Target ` or an ``ALIAS``. +```` may not be an ``ALIAS``. + +An ``ALIAS`` to a non-``GLOBAL`` :ref:`Imported Target ` +has scope in the directory in which the alias is created and below. +The :prop_tgt:`ALIAS_GLOBAL` target property can be used to check if the +alias is global or not. + ``ALIAS`` targets can be used as targets to read properties from, executables for custom commands and custom targets. They can also be tested for existence with the regular :command:`if(TARGET)` subcommand. diff --git a/Help/command/add_library.rst b/Help/command/add_library.rst index 7274e447400..01c415a929b 100644 --- a/Help/command/add_library.rst +++ b/Help/command/add_library.rst @@ -139,8 +139,13 @@ Alias Libraries Creates an :ref:`Alias Target `, such that ```` can be used to refer to ```` in subsequent commands. The ```` does not appear in the generated buildsystem as a make target. The ```` -may not be a non-``GLOBAL`` :ref:`Imported Target ` or an -``ALIAS``. +may not be an ``ALIAS``. + +An ``ALIAS`` to a non-``GLOBAL`` :ref:`Imported Target ` +has scope in the directory in which the alias is created and below. +The :prop_tgt:`ALIAS_GLOBAL` target property can be used to check if the +alias is global or not. + ``ALIAS`` targets can be used as linkable targets and as targets to read properties from. They can also be tested for existence with the regular :command:`if(TARGET)` subcommand. The ```` may not be used diff --git a/Help/command/add_link_options.rst b/Help/command/add_link_options.rst index a83005b0682..faa4afb2446 100644 --- a/Help/command/add_link_options.rst +++ b/Help/command/add_link_options.rst @@ -26,6 +26,8 @@ the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual for available expressions. See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem properties. +.. include:: DEVICE_LINK_OPTIONS.txt + .. include:: OPTIONS_SHELL.txt .. include:: LINK_OPTIONS_LINKER.txt diff --git a/Help/command/cmake_language.rst b/Help/command/cmake_language.rst new file mode 100644 index 00000000000..09880975f47 --- /dev/null +++ b/Help/command/cmake_language.rst @@ -0,0 +1,99 @@ +cmake_language +-------------- + +Call meta-operations on CMake commands. + +Synopsis +^^^^^^^^ + +.. parsed-literal:: + + cmake_language(`CALL`_ [...]) + cmake_language(`EVAL`_ CODE ...) + +Introduction +^^^^^^^^^^^^ + +This command will call meta-operations on built-in CMake commands or +those created via the :command:`macro` or :command:`function` commands. + +``cmake_language`` does not introduce a new variable or policy scope. + +Calling Commands +^^^^^^^^^^^^^^^^ + +.. _CALL: + +.. code-block:: cmake + + cmake_language(CALL [...]) + +Calls the named ```` with the given arguments (if any). +For example, the code: + +.. code-block:: cmake + + set(message_command "message") + cmake_language(CALL ${message_command} STATUS "Hello World!") + +is equivalent to + +.. code-block:: cmake + + message(STATUS "Hello World!") + +.. note:: + To ensure consistency of the code, the following commands are not allowed: + + * ``if`` / ``elseif`` / ``else`` / ``endif`` + * ``while`` / ``endwhile`` + * ``foreach`` / ``endforeach`` + * ``function`` / ``endfunction`` + * ``macro`` / ``endmacro`` + +Evaluating Code +^^^^^^^^^^^^^^^ + +.. _EVAL: + +.. code-block:: cmake + + cmake_language(EVAL CODE ...) + +Evaluates the ``...`` as CMake code. + +For example, the code: + +.. code-block:: cmake + + set(A TRUE) + set(B TRUE) + set(C TRUE) + set(condition "(A AND B) OR C") + + cmake_language(EVAL CODE " + if (${condition}) + message(STATUS TRUE) + else() + message(STATUS FALSE) + endif()" + ) + +is equivalent to + +.. code-block:: cmake + + set(A TRUE) + set(B TRUE) + set(C TRUE) + set(condition "(A AND B) OR C") + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/eval.cmake " + if (${condition}) + message(STATUS TRUE) + else() + message(STATUS FALSE) + endif()" + ) + + include(${CMAKE_CURRENT_BINARY_DIR}/eval.cmake) diff --git a/Help/command/ctest_test.rst b/Help/command/ctest_test.rst index 5c67b2c7068..3589296ad54 100644 --- a/Help/command/ctest_test.rst +++ b/Help/command/ctest_test.rst @@ -20,6 +20,7 @@ Perform the :ref:`CTest Test Step` as a :ref:`Dashboard Client`. [RESOURCE_SPEC_FILE ] [TEST_LOAD ] [SCHEDULE_RANDOM ] + [STOP_ON_FAILURE] [STOP_TIME ] [RETURN_VALUE ] [CAPTURE_CMAKE_ERROR ] @@ -119,6 +120,9 @@ The options are: Launch tests in a random order. This may be useful for detecting implicit test dependencies. +``STOP_ON_FAILURE`` + Stop the execution of the tests once one has failed. + ``STOP_TIME `` Specify a time of day at which the tests should all stop running. diff --git a/Help/command/execute_process.rst b/Help/command/execute_process.rst index 14f879d03fb..b32025fd4b7 100644 --- a/Help/command/execute_process.rst +++ b/Help/command/execute_process.rst @@ -21,7 +21,9 @@ Execute one or more child processes. [COMMAND_ECHO ] [OUTPUT_STRIP_TRAILING_WHITESPACE] [ERROR_STRIP_TRAILING_WHITESPACE] - [ENCODING ]) + [ENCODING ] + [ECHO_OUTPUT_VARIABLE] + [ECHO_ERROR_VARIABLE]) Runs the given sequence of one or more commands. @@ -105,6 +107,15 @@ Options: for this encoding. In CMake 3.11.0, ``UTF-8`` was added for consistency with the `UTF-8 RFC `_ naming convention. +``ECHO_OUTPUT_VARIABLE``, ``ECHO_ERROR_VARIABLE`` + The standard output or standard error will not be exclusively redirected to + the configured variables. + + The output will be duplicated, it will be sent into the configured variables + and also on standard output or standard error. + + This is analogous to the ``tee`` Unix command. + If more than one ``OUTPUT_*`` or ``ERROR_*`` option is given for the same pipe the precedence is not specified. If no ``OUTPUT_*`` or ``ERROR_*`` options are given the output will diff --git a/Help/command/file.rst b/Help/command/file.rst index df7d8bad922..693c059c524 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -19,6 +19,7 @@ Synopsis file({`WRITE`_ | `APPEND`_} ...) file({`TOUCH`_ | `TOUCH_NOCREATE`_} [...]) file(`GENERATE`_ OUTPUT [...]) + file(`CONFIGURE`_ OUTPUT CONTENT [...]) `Filesystem`_ file({`GLOB`_ | `GLOB_RECURSE`_} [...] [...]) @@ -41,6 +42,10 @@ Synopsis `Locking`_ file(`LOCK`_ [...]) + `Archiving`_ + file(`ARCHIVE_CREATE`_ OUTPUT PATHS ... [...]) + file(`ARCHIVE_EXTRACT`_ INPUT [...]) + Reading ^^^^^^^ @@ -54,7 +59,9 @@ Reading Read content from a file called ```` and store it in a ````. Optionally start from the given ```` and read at most ```` bytes. The ``HEX`` option causes data to -be converted to a hexadecimal representation (useful for binary data). +be converted to a hexadecimal representation (useful for binary data). If the +``HEX`` option is specified, letters in the output (``a`` through ``f``) are in +lowercase. .. _STRINGS: @@ -243,8 +250,8 @@ be resolved. See below for a full description of how they work. List of post-exclude regexes through which to filter the names of resolved dependencies. -These arguments can be used to blacklist unwanted system libraries when -resolving the dependencies, or to whitelist libraries from a specific +These arguments can be used to exclude unwanted system libraries when +resolving the dependencies, or to include libraries from a specific directory. The filtering works as follows: 1. If the not-yet-resolved dependency matches any of the @@ -395,8 +402,8 @@ dependency resolution: Determines the path to the tool to use for dependency resolution. This is the actual path to ``objdump``, ``dumpbin``, or ``otool``. - If this variable is not specified, it is determined automatically by system - introspection. + If this variable is not specified, it is determined by the value of + ``CMAKE_OBJDUMP`` if set, else by system introspection. Writing ^^^^^^^ @@ -482,6 +489,44 @@ generation phase. The output file will not yet have been written when the ``file(GENERATE)`` command returns, it is written only after processing all of a project's ``CMakeLists.txt`` files. +.. _CONFIGURE: + +.. code-block:: cmake + + file(CONFIGURE OUTPUT output-file + CONTENT content + [ESCAPE_QUOTES] [@ONLY] + [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ]) + +Generate an output file using the input given by ``CONTENT`` and substitute +variable values referenced as ``@VAR@`` or ``${VAR}`` contained therein. The +substitution rules behave the same as the :command:`configure_file` command. +In order to match :command:`configure_file`'s behavior, generator expressions +are not supported for both ``OUTPUT`` and ``CONTENT``. + +The arguments are: + +``OUTPUT `` + Specify the output file name to generate. A relative path is treated with + respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`. + ```` does not support generator expressions. + +``CONTENT `` + Use the content given explicitly as input. + ```` does not support generator expressions. + +``ESCAPE_QUOTES`` + Escape any substituted quotes with backslashes (C-style). + +``@ONLY`` + Restrict variable replacement to references of the form ``@VAR@``. + This is useful for configuring scripts that use ``${VAR}`` syntax. + +``NEWLINE_STYLE - - - diff --git a/Utilities/cmbzip2/bzdiff b/Utilities/cmbzip2/bzdiff deleted file mode 100644 index c4c9964900c..00000000000 --- a/Utilities/cmbzip2/bzdiff +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/sh -# sh is buggy on RS/6000 AIX 3.2. Replace above line with #!/bin/ksh - -# Bzcmp/diff wrapped for bzip2, -# adapted from zdiff by Philippe Troin for Debian GNU/Linux. - -# Bzcmp and bzdiff are used to invoke the cmp or the diff pro- -# gram on compressed files. All options specified are passed -# directly to cmp or diff. If only 1 file is specified, then -# the files compared are file1 and an uncompressed file1.gz. -# If two files are specified, then they are uncompressed (if -# necessary) and fed to cmp or diff. The exit status from cmp -# or diff is preserved. - -PATH="/usr/bin:/bin:$PATH"; export PATH -prog=`echo $0 | sed 's|.*/||'` -case "$prog" in - *cmp) comp=${CMP-cmp} ;; - *) comp=${DIFF-diff} ;; -esac - -OPTIONS= -FILES= -for ARG -do - case "$ARG" in - -*) OPTIONS="$OPTIONS $ARG";; - *) if test -f "$ARG"; then - FILES="$FILES $ARG" - else - echo "${prog}: $ARG not found or not a regular file" - exit 1 - fi ;; - esac -done -if test -z "$FILES"; then - echo "Usage: $prog [${comp}_options] file [file]" - exit 1 -fi -tmp=`mktemp ${TMPDIR:-/tmp}/bzdiff.XXXXXXXXXX` || { - echo 'cannot create a temporary file' >&2 - exit 1 -} -set $FILES -if test $# -eq 1; then - FILE=`echo "$1" | sed 's/.bz2$//'` - bzip2 -cd "$FILE.bz2" | $comp $OPTIONS - "$FILE" - STAT="$?" - -elif test $# -eq 2; then - case "$1" in - *.bz2) - case "$2" in - *.bz2) - F=`echo "$2" | sed 's|.*/||;s|.bz2$||'` - bzip2 -cdfq "$2" > $tmp - bzip2 -cdfq "$1" | $comp $OPTIONS - $tmp - STAT="$?" - /bin/rm -f $tmp;; - - *) bzip2 -cdfq "$1" | $comp $OPTIONS - "$2" - STAT="$?";; - esac;; - *) case "$2" in - *.bz2) - bzip2 -cdfq "$2" | $comp $OPTIONS "$1" - - STAT="$?";; - *) $comp $OPTIONS "$1" "$2" - STAT="$?";; - esac;; - esac - exit "$STAT" -else - echo "Usage: $prog [${comp}_options] file [file]" - exit 1 -fi diff --git a/Utilities/cmbzip2/bzdiff.1 b/Utilities/cmbzip2/bzdiff.1 deleted file mode 100644 index adb7a8e724e..00000000000 --- a/Utilities/cmbzip2/bzdiff.1 +++ /dev/null @@ -1,47 +0,0 @@ -\"Shamelessly copied from zmore.1 by Philippe Troin -\"for Debian GNU/Linux -.TH BZDIFF 1 -.SH NAME -bzcmp, bzdiff \- compare bzip2 compressed files -.SH SYNOPSIS -.B bzcmp -[ cmp_options ] file1 -[ file2 ] -.br -.B bzdiff -[ diff_options ] file1 -[ file2 ] -.SH DESCRIPTION -.I Bzcmp -and -.I bzdiff -are used to invoke the -.I cmp -or the -.I diff -program on bzip2 compressed files. All options specified are passed -directly to -.I cmp -or -.IR diff "." -If only 1 file is specified, then the files compared are -.I file1 -and an uncompressed -.IR file1 ".bz2." -If two files are specified, then they are uncompressed if necessary and fed to -.I cmp -or -.IR diff "." -The exit status from -.I cmp -or -.I diff -is preserved. -.SH "SEE ALSO" -cmp(1), diff(1), bzmore(1), bzless(1), bzgrep(1), bzip2(1) -.SH BUGS -Messages from the -.I cmp -or -.I diff -programs refer to temporary filenames instead of those specified. diff --git a/Utilities/cmbzip2/bzgrep b/Utilities/cmbzip2/bzgrep deleted file mode 100644 index 8ccf919ac7d..00000000000 --- a/Utilities/cmbzip2/bzgrep +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh - -# Bzgrep wrapped for bzip2, -# adapted from zgrep by Philippe Troin for Debian GNU/Linux. -## zgrep notice: -## zgrep -- a wrapper around a grep program that decompresses files as needed -## Adapted from a version sent by Charles Levert - -PATH="/usr/bin:$PATH"; export PATH - -prog=`echo $0 | sed 's|.*/||'` -case "$prog" in - *egrep) grep=${EGREP-egrep} ;; - *fgrep) grep=${FGREP-fgrep} ;; - *) grep=${GREP-grep} ;; -esac -pat="" -while test $# -ne 0; do - case "$1" in - -e | -f) opt="$opt $1"; shift; pat="$1" - if test "$grep" = grep; then # grep is buggy with -e on SVR4 - grep=egrep - fi;; - -A | -B) opt="$opt $1 $2"; shift;; - -*) opt="$opt $1";; - *) if test -z "$pat"; then - pat="$1" - else - break; - fi;; - esac - shift -done - -if test -z "$pat"; then - echo "grep through bzip2 files" - echo "usage: $prog [grep_options] pattern [files]" - exit 1 -fi - -list=0 -silent=0 -op=`echo "$opt" | sed -e 's/ //g' -e 's/-//g'` -case "$op" in - *l*) list=1 -esac -case "$op" in - *h*) silent=1 -esac - -if test $# -eq 0; then - bzip2 -cdfq | $grep $opt "$pat" - exit $? -fi - -res=0 -for i do - if test -f "$i"; then :; else if test -f "$i.bz2"; then i="$i.bz2"; fi; fi - if test $list -eq 1; then - bzip2 -cdfq "$i" | $grep $opt "$pat" 2>&1 > /dev/null && echo $i - r=$? - elif test $# -eq 1 -o $silent -eq 1; then - bzip2 -cdfq "$i" | $grep $opt "$pat" - r=$? - else - j=${i//\\/\\\\} - j=${j//|/\\|} - j=${j//&/\\&} - j=`printf "%s" "$j" | tr '\n' ' '` - bzip2 -cdfq "$i" | $grep $opt "$pat" | sed "s|^|${j}:|" - r=$? - fi - test "$r" -ne 0 && res="$r" -done -exit $res diff --git a/Utilities/cmbzip2/bzgrep.1 b/Utilities/cmbzip2/bzgrep.1 deleted file mode 100644 index 930af8c7fcb..00000000000 --- a/Utilities/cmbzip2/bzgrep.1 +++ /dev/null @@ -1,56 +0,0 @@ -\"Shamelessly copied from zmore.1 by Philippe Troin -\"for Debian GNU/Linux -.TH BZGREP 1 -.SH NAME -bzgrep, bzfgrep, bzegrep \- search possibly bzip2 compressed files for a regular expression -.SH SYNOPSIS -.B bzgrep -[ grep_options ] -.BI [\ -e\ ] " pattern" -.IR filename ".\|.\|." -.br -.B bzegrep -[ egrep_options ] -.BI [\ -e\ ] " pattern" -.IR filename ".\|.\|." -.br -.B bzfgrep -[ fgrep_options ] -.BI [\ -e\ ] " pattern" -.IR filename ".\|.\|." -.SH DESCRIPTION -.IR Bzgrep -is used to invoke the -.I grep -on bzip2-compressed files. All options specified are passed directly to -.I grep. -If no file is specified, then the standard input is decompressed -if necessary and fed to grep. -Otherwise the given files are uncompressed if necessary and fed to -.I grep. -.PP -If -.I bzgrep -is invoked as -.I bzegrep -or -.I bzfgrep -then -.I egrep -or -.I fgrep -is used instead of -.I grep. -If the GREP environment variable is set, -.I bzgrep -uses it as the -.I grep -program to be invoked. For example: - - for sh: GREP=fgrep bzgrep string files - for csh: (setenv GREP fgrep; bzgrep string files) -.SH AUTHOR -Charles Levert (charles@comm.polymtl.ca). Adapted to bzip2 by Philippe -Troin for Debian GNU/Linux. -.SH "SEE ALSO" -grep(1), egrep(1), fgrep(1), bzdiff(1), bzmore(1), bzless(1), bzip2(1) diff --git a/Utilities/cmbzip2/bzip.css b/Utilities/cmbzip2/bzip.css deleted file mode 100644 index 4feb40165c8..00000000000 --- a/Utilities/cmbzip2/bzip.css +++ /dev/null @@ -1,74 +0,0 @@ -/* Colours: -#74240f dark brown h1, h2, h3, h4 -#336699 medium blue links -#339999 turquoise link hover colour -#202020 almost black general text -#761596 purple md5sum text -#626262 dark gray pre border -#eeeeee very light gray pre background -#f2f2f9 very light blue nav table background -#3366cc medium blue nav table border -*/ - -a, a:link, a:visited, a:active { color: #336699; } -a:hover { color: #339999; } - -body { font: 80%/126% sans-serif; } -h1, h2, h3, h4 { color: #74240f; } - -dt { color: #336699; font-weight: bold } -dd { - margin-left: 1.5em; - padding-bottom: 0.8em; -} - -/* -- ruler -- */ -div.hr_blue { - height: 3px; - background:#ffffff url("/images/hr_blue.png") repeat-x; } -div.hr_blue hr { display:none; } - -/* release styles */ -#release p { margin-top: 0.4em; } -#release .md5sum { color: #761596; } - - -/* ------ styles for docs|manuals|howto ------ */ -/* -- lists -- */ -ul { - margin: 0px 4px 16px 16px; - padding: 0px; - list-style: url("/images/li-blue.png"); -} -ul li { - margin-bottom: 10px; -} -ul ul { - list-style-type: none; - list-style-image: none; - margin-left: 0px; -} - -/* header / footer nav tables */ -table.nav { - border: solid 1px #3366cc; - background: #f2f2f9; - background-color: #f2f2f9; - margin-bottom: 0.5em; -} -/* don't have underlined links in chunked nav menus */ -table.nav a { text-decoration: none; } -table.nav a:hover { text-decoration: underline; } -table.nav td { font-size: 85%; } - -code, tt, pre { font-size: 120%; } -code, tt { color: #761596; } - -div.literallayout, pre.programlisting, pre.screen { - color: #000000; - padding: 0.5em; - background: #eeeeee; - border: 1px solid #626262; - background-color: #eeeeee; - margin: 4px 0px 4px 0px; -} diff --git a/Utilities/cmbzip2/bzip2.1 b/Utilities/cmbzip2/bzip2.1 deleted file mode 100644 index a313f2d5bed..00000000000 --- a/Utilities/cmbzip2/bzip2.1 +++ /dev/null @@ -1,454 +0,0 @@ -.PU -.TH bzip2 1 -.SH NAME -bzip2, bunzip2 \- a block-sorting file compressor, v1.0.4 -.br -bzcat \- decompresses files to stdout -.br -bzip2recover \- recovers data from damaged bzip2 files - -.SH SYNOPSIS -.ll +8 -.B bzip2 -.RB [ " \-cdfkqstvzVL123456789 " ] -[ -.I "filenames \&..." -] -.ll -8 -.br -.B bunzip2 -.RB [ " \-fkvsVL " ] -[ -.I "filenames \&..." -] -.br -.B bzcat -.RB [ " \-s " ] -[ -.I "filenames \&..." -] -.br -.B bzip2recover -.I "filename" - -.SH DESCRIPTION -.I bzip2 -compresses files using the Burrows-Wheeler block sorting -text compression algorithm, and Huffman coding. Compression is -generally considerably better than that achieved by more conventional -LZ77/LZ78-based compressors, and approaches the performance of the PPM -family of statistical compressors. - -The command-line options are deliberately very similar to -those of -.I GNU gzip, -but they are not identical. - -.I bzip2 -expects a list of file names to accompany the -command-line flags. Each file is replaced by a compressed version of -itself, with the name "original_name.bz2". -Each compressed file -has the same modification date, permissions, and, when possible, -ownership as the corresponding original, so that these properties can -be correctly restored at decompression time. File name handling is -naive in the sense that there is no mechanism for preserving original -file names, permissions, ownerships or dates in filesystems which lack -these concepts, or have serious file name length restrictions, such as -MS-DOS. - -.I bzip2 -and -.I bunzip2 -will by default not overwrite existing -files. If you want this to happen, specify the \-f flag. - -If no file names are specified, -.I bzip2 -compresses from standard -input to standard output. In this case, -.I bzip2 -will decline to -write compressed output to a terminal, as this would be entirely -incomprehensible and therefore pointless. - -.I bunzip2 -(or -.I bzip2 \-d) -decompresses all -specified files. Files which were not created by -.I bzip2 -will be detected and ignored, and a warning issued. -.I bzip2 -attempts to guess the filename for the decompressed file -from that of the compressed file as follows: - - filename.bz2 becomes filename - filename.bz becomes filename - filename.tbz2 becomes filename.tar - filename.tbz becomes filename.tar - anyothername becomes anyothername.out - -If the file does not end in one of the recognised endings, -.I .bz2, -.I .bz, -.I .tbz2 -or -.I .tbz, -.I bzip2 -complains that it cannot -guess the name of the original file, and uses the original name -with -.I .out -appended. - -As with compression, supplying no -filenames causes decompression from -standard input to standard output. - -.I bunzip2 -will correctly decompress a file which is the -concatenation of two or more compressed files. The result is the -concatenation of the corresponding uncompressed files. Integrity -testing (\-t) -of concatenated -compressed files is also supported. - -You can also compress or decompress files to the standard output by -giving the \-c flag. Multiple files may be compressed and -decompressed like this. The resulting outputs are fed sequentially to -stdout. Compression of multiple files -in this manner generates a stream -containing multiple compressed file representations. Such a stream -can be decompressed correctly only by -.I bzip2 -version 0.9.0 or -later. Earlier versions of -.I bzip2 -will stop after decompressing -the first file in the stream. - -.I bzcat -(or -.I bzip2 -dc) -decompresses all specified files to -the standard output. - -.I bzip2 -will read arguments from the environment variables -.I BZIP2 -and -.I BZIP, -in that order, and will process them -before any arguments read from the command line. This gives a -convenient way to supply default arguments. - -Compression is always performed, even if the compressed -file is slightly -larger than the original. Files of less than about one hundred bytes -tend to get larger, since the compression mechanism has a constant -overhead in the region of 50 bytes. Random data (including the output -of most file compressors) is coded at about 8.05 bits per byte, giving -an expansion of around 0.5%. - -As a self-check for your protection, -.I -bzip2 -uses 32-bit CRCs to -make sure that the decompressed version of a file is identical to the -original. This guards against corruption of the compressed data, and -against undetected bugs in -.I bzip2 -(hopefully very unlikely). The -chances of data corruption going undetected is microscopic, about one -chance in four billion for each file processed. Be aware, though, that -the check occurs upon decompression, so it can only tell you that -something is wrong. It can't help you -recover the original uncompressed -data. You can use -.I bzip2recover -to try to recover data from -damaged files. - -Return values: 0 for a normal exit, 1 for environmental problems (file -not found, invalid flags, I/O errors, &c), 2 to indicate a corrupt -compressed file, 3 for an internal consistency error (eg, bug) which -caused -.I bzip2 -to panic. - -.SH OPTIONS -.TP -.B \-c --stdout -Compress or decompress to standard output. -.TP -.B \-d --decompress -Force decompression. -.I bzip2, -.I bunzip2 -and -.I bzcat -are -really the same program, and the decision about what actions to take is -done on the basis of which name is used. This flag overrides that -mechanism, and forces -.I bzip2 -to decompress. -.TP -.B \-z --compress -The complement to \-d: forces compression, regardless of the -invocation name. -.TP -.B \-t --test -Check integrity of the specified file(s), but don't decompress them. -This really performs a trial decompression and throws away the result. -.TP -.B \-f --force -Force overwrite of output files. Normally, -.I bzip2 -will not overwrite -existing output files. Also forces -.I bzip2 -to break hard links -to files, which it otherwise wouldn't do. - -bzip2 normally declines to decompress files which don't have the -correct magic header bytes. If forced (-f), however, it will pass -such files through unmodified. This is how GNU gzip behaves. -.TP -.B \-k --keep -Keep (don't delete) input files during compression -or decompression. -.TP -.B \-s --small -Reduce memory usage, for compression, decompression and testing. Files -are decompressed and tested using a modified algorithm which only -requires 2.5 bytes per block byte. This means any file can be -decompressed in 2300k of memory, albeit at about half the normal speed. - -During compression, \-s selects a block size of 200k, which limits -memory use to around the same figure, at the expense of your compression -ratio. In short, if your machine is low on memory (8 megabytes or -less), use \-s for everything. See MEMORY MANAGEMENT below. -.TP -.B \-q --quiet -Suppress non-essential warning messages. Messages pertaining to -I/O errors and other critical events will not be suppressed. -.TP -.B \-v --verbose -Verbose mode -- show the compression ratio for each file processed. -Further \-v's increase the verbosity level, spewing out lots of -information which is primarily of interest for diagnostic purposes. -.TP -.B \-L --license -V --version -Display the software version, license terms and conditions. -.TP -.B \-1 (or \-\-fast) to \-9 (or \-\-best) -Set the block size to 100 k, 200 k .. 900 k when compressing. Has no -effect when decompressing. See MEMORY MANAGEMENT below. -The \-\-fast and \-\-best aliases are primarily for GNU gzip -compatibility. In particular, \-\-fast doesn't make things -significantly faster. -And \-\-best merely selects the default behaviour. -.TP -.B \-- -Treats all subsequent arguments as file names, even if they start -with a dash. This is so you can handle files with names beginning -with a dash, for example: bzip2 \-- \-myfilename. -.TP -.B \--repetitive-fast --repetitive-best -These flags are redundant in versions 0.9.5 and above. They provided -some coarse control over the behaviour of the sorting algorithm in -earlier versions, which was sometimes useful. 0.9.5 and above have an -improved algorithm which renders these flags irrelevant. - -.SH MEMORY MANAGEMENT -.I bzip2 -compresses large files in blocks. The block size affects -both the compression ratio achieved, and the amount of memory needed for -compression and decompression. The flags \-1 through \-9 -specify the block size to be 100,000 bytes through 900,000 bytes (the -default) respectively. At decompression time, the block size used for -compression is read from the header of the compressed file, and -.I bunzip2 -then allocates itself just enough memory to decompress -the file. Since block sizes are stored in compressed files, it follows -that the flags \-1 to \-9 are irrelevant to and so ignored -during decompression. - -Compression and decompression requirements, -in bytes, can be estimated as: - - Compression: 400k + ( 8 x block size ) - - Decompression: 100k + ( 4 x block size ), or - 100k + ( 2.5 x block size ) - -Larger block sizes give rapidly diminishing marginal returns. Most of -the compression comes from the first two or three hundred k of block -size, a fact worth bearing in mind when using -.I bzip2 -on small machines. -It is also important to appreciate that the decompression memory -requirement is set at compression time by the choice of block size. - -For files compressed with the default 900k block size, -.I bunzip2 -will require about 3700 kbytes to decompress. To support decompression -of any file on a 4 megabyte machine, -.I bunzip2 -has an option to -decompress using approximately half this amount of memory, about 2300 -kbytes. Decompression speed is also halved, so you should use this -option only where necessary. The relevant flag is -s. - -In general, try and use the largest block size memory constraints allow, -since that maximises the compression achieved. Compression and -decompression speed are virtually unaffected by block size. - -Another significant point applies to files which fit in a single block --- that means most files you'd encounter using a large block size. The -amount of real memory touched is proportional to the size of the file, -since the file is smaller than a block. For example, compressing a file -20,000 bytes long with the flag -9 will cause the compressor to -allocate around 7600k of memory, but only touch 400k + 20000 * 8 = 560 -kbytes of it. Similarly, the decompressor will allocate 3700k but only -touch 100k + 20000 * 4 = 180 kbytes. - -Here is a table which summarises the maximum memory usage for different -block sizes. Also recorded is the total compressed size for 14 files of -the Calgary Text Compression Corpus totalling 3,141,622 bytes. This -column gives some feel for how compression varies with block size. -These figures tend to understate the advantage of larger block sizes for -larger files, since the Corpus is dominated by smaller files. - - Compress Decompress Decompress Corpus - Flag usage usage -s usage Size - - -1 1200k 500k 350k 914704 - -2 2000k 900k 600k 877703 - -3 2800k 1300k 850k 860338 - -4 3600k 1700k 1100k 846899 - -5 4400k 2100k 1350k 845160 - -6 5200k 2500k 1600k 838626 - -7 6100k 2900k 1850k 834096 - -8 6800k 3300k 2100k 828642 - -9 7600k 3700k 2350k 828642 - -.SH RECOVERING DATA FROM DAMAGED FILES -.I bzip2 -compresses files in blocks, usually 900kbytes long. Each -block is handled independently. If a media or transmission error causes -a multi-block .bz2 -file to become damaged, it may be possible to -recover data from the undamaged blocks in the file. - -The compressed representation of each block is delimited by a 48-bit -pattern, which makes it possible to find the block boundaries with -reasonable certainty. Each block also carries its own 32-bit CRC, so -damaged blocks can be distinguished from undamaged ones. - -.I bzip2recover -is a simple program whose purpose is to search for -blocks in .bz2 files, and write each block out into its own .bz2 -file. You can then use -.I bzip2 -\-t -to test the -integrity of the resulting files, and decompress those which are -undamaged. - -.I bzip2recover -takes a single argument, the name of the damaged file, -and writes a number of files "rec00001file.bz2", -"rec00002file.bz2", etc, containing the extracted blocks. -The output filenames are designed so that the use of -wildcards in subsequent processing -- for example, -"bzip2 -dc rec*file.bz2 > recovered_data" -- processes the files in -the correct order. - -.I bzip2recover -should be of most use dealing with large .bz2 -files, as these will contain many blocks. It is clearly -futile to use it on damaged single-block files, since a -damaged block cannot be recovered. If you wish to minimise -any potential data loss through media or transmission errors, -you might consider compressing with a smaller -block size. - -.SH PERFORMANCE NOTES -The sorting phase of compression gathers together similar strings in the -file. Because of this, files containing very long runs of repeated -symbols, like "aabaabaabaab ..." (repeated several hundred times) may -compress more slowly than normal. Versions 0.9.5 and above fare much -better than previous versions in this respect. The ratio between -worst-case and average-case compression time is in the region of 10:1. -For previous versions, this figure was more like 100:1. You can use the -\-vvvv option to monitor progress in great detail, if you want. - -Decompression speed is unaffected by these phenomena. - -.I bzip2 -usually allocates several megabytes of memory to operate -in, and then charges all over it in a fairly random fashion. This means -that performance, both for compressing and decompressing, is largely -determined by the speed at which your machine can service cache misses. -Because of this, small changes to the code to reduce the miss rate have -been observed to give disproportionately large performance improvements. -I imagine -.I bzip2 -will perform best on machines with very large caches. - -.SH CAVEATS -I/O error messages are not as helpful as they could be. -.I bzip2 -tries hard to detect I/O errors and exit cleanly, but the details of -what the problem is sometimes seem rather misleading. - -This manual page pertains to version 1.0.4 of -.I bzip2. -Compressed data created by this version is entirely forwards and -backwards compatible with the previous public releases, versions -0.1pl2, 0.9.0, 0.9.5, 1.0.0, 1.0.1, 1.0.2 and 1.0.3, but with the following -exception: 0.9.0 and above can correctly decompress multiple -concatenated compressed files. 0.1pl2 cannot do this; it will stop -after decompressing just the first file in the stream. - -.I bzip2recover -versions prior to 1.0.2 used 32-bit integers to represent -bit positions in compressed files, so they could not handle compressed -files more than 512 megabytes long. Versions 1.0.2 and above use -64-bit ints on some platforms which support them (GNU supported -targets, and Windows). To establish whether or not bzip2recover was -built with such a limitation, run it without arguments. In any event -you can build yourself an unlimited version if you can recompile it -with MaybeUInt64 set to be an unsigned 64-bit integer. - - - -.SH AUTHOR -Julian Seward, jsewardbzip.org. - -http://www.bzip.org - -The ideas embodied in -.I bzip2 -are due to (at least) the following -people: Michael Burrows and David Wheeler (for the block sorting -transformation), David Wheeler (again, for the Huffman coder), Peter -Fenwick (for the structured coding model in the original -.I bzip, -and many refinements), and Alistair Moffat, Radford Neal and Ian Witten -(for the arithmetic coder in the original -.I bzip). -I am much -indebted for their help, support and advice. See the manual in the -source distribution for pointers to sources of documentation. Christian -von Roques encouraged me to look for faster sorting algorithms, so as to -speed up compression. Bela Lubkin encouraged me to improve the -worst-case compression performance. -Donna Robinson XMLised the documentation. -The bz* scripts are derived from those of GNU gzip. -Many people sent patches, helped -with portability problems, lent machines, gave advice and were generally -helpful. diff --git a/Utilities/cmbzip2/bzip2.1.preformatted b/Utilities/cmbzip2/bzip2.1.preformatted deleted file mode 100644 index 15e16e50aba..00000000000 --- a/Utilities/cmbzip2/bzip2.1.preformatted +++ /dev/null @@ -1,399 +0,0 @@ -bzip2(1) bzip2(1) - - - -NNAAMMEE - bzip2, bunzip2 − a blockâ€sorting file compressor, v1.0.4 - bzcat − decompresses files to stdout - bzip2recover − recovers data from damaged bzip2 files - - -SSYYNNOOPPSSIISS - bbzziipp22 [ −−ccddffkkqqssttvvzzVVLL112233445566778899 ] [ _f_i_l_e_n_a_m_e_s _._._. ] - bbuunnzziipp22 [ −−ffkkvvssVVLL ] [ _f_i_l_e_n_a_m_e_s _._._. ] - bbzzccaatt [ −−ss ] [ _f_i_l_e_n_a_m_e_s _._._. ] - bbzziipp22rreeccoovveerr _f_i_l_e_n_a_m_e - - -DDEESSCCRRIIPPTTIIOONN - _b_z_i_p_2 compresses files using the Burrowsâ€Wheeler block - sorting text compression algorithm, and Huffman coding. - Compression is generally considerably better than that - achieved by more conventional LZ77/LZ78â€based compressors, - and approaches the performance of the PPM family of sta­ - tistical compressors. - - The commandâ€line options are deliberately very similar to - those of _G_N_U _g_z_i_p_, but they are not identical. - - _b_z_i_p_2 expects a list of file names to accompany the com­ - mandâ€line flags. Each file is replaced by a compressed - version of itself, with the name "original_name.bz2". - Each compressed file has the same modification date, per­ - missions, and, when possible, ownership as the correspond­ - ing original, so that these properties can be correctly - restored at decompression time. File name handling is - naive in the sense that there is no mechanism for preserv­ - ing original file names, permissions, ownerships or dates - in filesystems which lack these concepts, or have serious - file name length restrictions, such as MSâ€DOS. - - _b_z_i_p_2 and _b_u_n_z_i_p_2 will by default not overwrite existing - files. If you want this to happen, specify the −f flag. - - If no file names are specified, _b_z_i_p_2 compresses from - standard input to standard output. In this case, _b_z_i_p_2 - will decline to write compressed output to a terminal, as - this would be entirely incomprehensible and therefore - pointless. - - _b_u_n_z_i_p_2 (or _b_z_i_p_2 _−_d_) decompresses all specified files. - Files which were not created by _b_z_i_p_2 will be detected and - ignored, and a warning issued. _b_z_i_p_2 attempts to guess - the filename for the decompressed file from that of the - compressed file as follows: - - filename.bz2 becomes filename - filename.bz becomes filename - filename.tbz2 becomes filename.tar - filename.tbz becomes filename.tar - anyothername becomes anyothername.out - - If the file does not end in one of the recognised endings, - _._b_z_2_, _._b_z_, _._t_b_z_2 or _._t_b_z_, _b_z_i_p_2 complains that it cannot - guess the name of the original file, and uses the original - name with _._o_u_t appended. - - As with compression, supplying no filenames causes decom­ - pression from standard input to standard output. - - _b_u_n_z_i_p_2 will correctly decompress a file which is the con­ - catenation of two or more compressed files. The result is - the concatenation of the corresponding uncompressed files. - Integrity testing (−t) of concatenated compressed files is - also supported. - - You can also compress or decompress files to the standard - output by giving the −c flag. Multiple files may be com­ - pressed and decompressed like this. The resulting outputs - are fed sequentially to stdout. Compression of multiple - files in this manner generates a stream containing multi­ - ple compressed file representations. Such a stream can be - decompressed correctly only by _b_z_i_p_2 version 0.9.0 or - later. Earlier versions of _b_z_i_p_2 will stop after decom­ - pressing the first file in the stream. - - _b_z_c_a_t (or _b_z_i_p_2 _â€_d_c_) decompresses all specified files to - the standard output. - - _b_z_i_p_2 will read arguments from the environment variables - _B_Z_I_P_2 and _B_Z_I_P_, in that order, and will process them - before any arguments read from the command line. This - gives a convenient way to supply default arguments. - - Compression is always performed, even if the compressed - file is slightly larger than the original. Files of less - than about one hundred bytes tend to get larger, since the - compression mechanism has a constant overhead in the - region of 50 bytes. Random data (including the output of - most file compressors) is coded at about 8.05 bits per - byte, giving an expansion of around 0.5%. - - As a selfâ€check for your protection, _b_z_i_p_2 uses 32â€bit - CRCs to make sure that the decompressed version of a file - is identical to the original. This guards against corrup­ - tion of the compressed data, and against undetected bugs - in _b_z_i_p_2 (hopefully very unlikely). The chances of data - corruption going undetected is microscopic, about one - chance in four billion for each file processed. Be aware, - though, that the check occurs upon decompression, so it - can only tell you that something is wrong. It can’t help - you recover the original uncompressed data. You can use - _b_z_i_p_2_r_e_c_o_v_e_r to try to recover data from damaged files. - - Return values: 0 for a normal exit, 1 for environmental - problems (file not found, invalid flags, I/O errors, &c), - 2 to indicate a corrupt compressed file, 3 for an internal - consistency error (eg, bug) which caused _b_z_i_p_2 to panic. - - -OOPPTTIIOONNSS - −−cc â€â€â€â€ssttddoouutt - Compress or decompress to standard output. - - −−dd â€â€â€â€ddeeccoommpprreessss - Force decompression. _b_z_i_p_2_, _b_u_n_z_i_p_2 and _b_z_c_a_t are - really the same program, and the decision about - what actions to take is done on the basis of which - name is used. This flag overrides that mechanism, - and forces _b_z_i_p_2 to decompress. - - −−zz â€â€â€â€ccoommpprreessss - The complement to −d: forces compression, - regardless of the invocation name. - - −−tt â€â€â€â€tteesstt - Check integrity of the specified file(s), but don’t - decompress them. This really performs a trial - decompression and throws away the result. - - −−ff â€â€â€â€ffoorrccee - Force overwrite of output files. Normally, _b_z_i_p_2 - will not overwrite existing output files. Also - forces _b_z_i_p_2 to break hard links to files, which it - otherwise wouldn’t do. - - bzip2 normally declines to decompress files which - don’t have the correct magic header bytes. If - forced (â€f), however, it will pass such files - through unmodified. This is how GNU gzip behaves. - - −−kk â€â€â€â€kkeeeepp - Keep (don’t delete) input files during compression - or decompression. - - −−ss â€â€â€â€ssmmaallll - Reduce memory usage, for compression, decompression - and testing. Files are decompressed and tested - using a modified algorithm which only requires 2.5 - bytes per block byte. This means any file can be - decompressed in 2300k of memory, albeit at about - half the normal speed. - - During compression, −s selects a block size of - 200k, which limits memory use to around the same - figure, at the expense of your compression ratio. - In short, if your machine is low on memory (8 - megabytes or less), use −s for everything. See - MEMORY MANAGEMENT below. - - −−qq â€â€â€â€qquuiieett - Suppress nonâ€essential warning messages. Messages - pertaining to I/O errors and other critical events - will not be suppressed. - - −−vv â€â€â€â€vveerrbboossee - Verbose mode â€â€ show the compression ratio for each - file processed. Further −v’s increase the ver­ - bosity level, spewing out lots of information which - is primarily of interest for diagnostic purposes. - - −−LL â€â€â€â€lliicceennssee â€â€VV â€â€â€â€vveerrssiioonn - Display the software version, license terms and - conditions. - - −−11 ((oorr −−−−ffaasstt)) ttoo −−99 ((oorr −−−−bbeesstt)) - Set the block size to 100 k, 200 k .. 900 k when - compressing. Has no effect when decompressing. - See MEMORY MANAGEMENT below. The −−fast and −−best - aliases are primarily for GNU gzip compatibility. - In particular, −−fast doesn’t make things signifi­ - cantly faster. And −−best merely selects the - default behaviour. - - −− Treats all subsequent arguments as file names, even - if they start with a dash. This is so you can han­ - dle files with names beginning with a dash, for - example: bzip2 −†−myfilename. - - −−â€â€rreeppeettiittiivveeâ€â€ffaasstt â€â€â€â€rreeppeettiittiivveeâ€â€bbeesstt - These flags are redundant in versions 0.9.5 and - above. They provided some coarse control over the - behaviour of the sorting algorithm in earlier ver­ - sions, which was sometimes useful. 0.9.5 and above - have an improved algorithm which renders these - flags irrelevant. - - -MMEEMMOORRYY MMAANNAAGGEEMMEENNTT - _b_z_i_p_2 compresses large files in blocks. The block size - affects both the compression ratio achieved, and the - amount of memory needed for compression and decompression. - The flags −1 through −9 specify the block size to be - 100,000 bytes through 900,000 bytes (the default) respec­ - tively. At decompression time, the block size used for - compression is read from the header of the compressed - file, and _b_u_n_z_i_p_2 then allocates itself just enough memory - to decompress the file. Since block sizes are stored in - compressed files, it follows that the flags −1 to −9 are - irrelevant to and so ignored during decompression. - - Compression and decompression requirements, in bytes, can - be estimated as: - - Compression: 400k + ( 8 x block size ) - - Decompression: 100k + ( 4 x block size ), or - 100k + ( 2.5 x block size ) - - Larger block sizes give rapidly diminishing marginal - returns. Most of the compression comes from the first two - or three hundred k of block size, a fact worth bearing in - mind when using _b_z_i_p_2 on small machines. It is also - important to appreciate that the decompression memory - requirement is set at compression time by the choice of - block size. - - For files compressed with the default 900k block size, - _b_u_n_z_i_p_2 will require about 3700 kbytes to decompress. To - support decompression of any file on a 4 megabyte machine, - _b_u_n_z_i_p_2 has an option to decompress using approximately - half this amount of memory, about 2300 kbytes. Decompres­ - sion speed is also halved, so you should use this option - only where necessary. The relevant flag is â€s. - - In general, try and use the largest block size memory con­ - straints allow, since that maximises the compression - achieved. Compression and decompression speed are virtu­ - ally unaffected by block size. - - Another significant point applies to files which fit in a - single block â€â€ that means most files you’d encounter - using a large block size. The amount of real memory - touched is proportional to the size of the file, since the - file is smaller than a block. For example, compressing a - file 20,000 bytes long with the flag â€9 will cause the - compressor to allocate around 7600k of memory, but only - touch 400k + 20000 * 8 = 560 kbytes of it. Similarly, the - decompressor will allocate 3700k but only touch 100k + - 20000 * 4 = 180 kbytes. - - Here is a table which summarises the maximum memory usage - for different block sizes. Also recorded is the total - compressed size for 14 files of the Calgary Text Compres­ - sion Corpus totalling 3,141,622 bytes. This column gives - some feel for how compression varies with block size. - These figures tend to understate the advantage of larger - block sizes for larger files, since the Corpus is domi­ - nated by smaller files. - - Compress Decompress Decompress Corpus - Flag usage usage â€s usage Size - - â€1 1200k 500k 350k 914704 - â€2 2000k 900k 600k 877703 - â€3 2800k 1300k 850k 860338 - â€4 3600k 1700k 1100k 846899 - â€5 4400k 2100k 1350k 845160 - â€6 5200k 2500k 1600k 838626 - â€7 6100k 2900k 1850k 834096 - â€8 6800k 3300k 2100k 828642 - â€9 7600k 3700k 2350k 828642 - - -RREECCOOVVEERRIINNGG DDAATTAA FFRROOMM DDAAMMAAGGEEDD FFIILLEESS - _b_z_i_p_2 compresses files in blocks, usually 900kbytes long. - Each block is handled independently. If a media or trans­ - mission error causes a multiâ€block .bz2 file to become - damaged, it may be possible to recover data from the - undamaged blocks in the file. - - The compressed representation of each block is delimited - by a 48â€bit pattern, which makes it possible to find the - block boundaries with reasonable certainty. Each block - also carries its own 32â€bit CRC, so damaged blocks can be - distinguished from undamaged ones. - - _b_z_i_p_2_r_e_c_o_v_e_r is a simple program whose purpose is to - search for blocks in .bz2 files, and write each block out - into its own .bz2 file. You can then use _b_z_i_p_2 −t to test - the integrity of the resulting files, and decompress those - which are undamaged. - - _b_z_i_p_2_r_e_c_o_v_e_r takes a single argument, the name of the dam­ - aged file, and writes a number of files - "rec00001file.bz2", "rec00002file.bz2", etc, containing - the extracted blocks. The output filenames are - designed so that the use of wildcards in subsequent pro­ - cessing â€â€ for example, "bzip2 â€dc rec*file.bz2 > recov­ - ered_data" â€â€ processes the files in the correct order. - - _b_z_i_p_2_r_e_c_o_v_e_r should be of most use dealing with large .bz2 - files, as these will contain many blocks. It is clearly - futile to use it on damaged singleâ€block files, since a - damaged block cannot be recovered. If you wish to min­ - imise any potential data loss through media or transmis­ - sion errors, you might consider compressing with a smaller - block size. - - -PPEERRFFOORRMMAANNCCEE NNOOTTEESS - The sorting phase of compression gathers together similar - strings in the file. Because of this, files containing - very long runs of repeated symbols, like "aabaabaabaab - ..." (repeated several hundred times) may compress more - slowly than normal. Versions 0.9.5 and above fare much - better than previous versions in this respect. The ratio - between worstâ€case and averageâ€case compression time is in - the region of 10:1. For previous versions, this figure - was more like 100:1. You can use the −vvvv option to mon­ - itor progress in great detail, if you want. - - Decompression speed is unaffected by these phenomena. - - _b_z_i_p_2 usually allocates several megabytes of memory to - operate in, and then charges all over it in a fairly ran­ - dom fashion. This means that performance, both for com­ - pressing and decompressing, is largely determined by the - speed at which your machine can service cache misses. - Because of this, small changes to the code to reduce the - miss rate have been observed to give disproportionately - large performance improvements. I imagine _b_z_i_p_2 will per­ - form best on machines with very large caches. - - -CCAAVVEEAATTSS - I/O error messages are not as helpful as they could be. - _b_z_i_p_2 tries hard to detect I/O errors and exit cleanly, - but the details of what the problem is sometimes seem - rather misleading. - - This manual page pertains to version 1.0.4 of _b_z_i_p_2_. Com­ - pressed data created by this version is entirely forwards - and backwards compatible with the previous public - releases, versions 0.1pl2, 0.9.0, 0.9.5, 1.0.0, 1.0.1, - 1.0.2 and 1.0.3, but with the following exception: 0.9.0 - and above can correctly decompress multiple concatenated - compressed files. 0.1pl2 cannot do this; it will stop - after decompressing just the first file in the stream. - - _b_z_i_p_2_r_e_c_o_v_e_r versions prior to 1.0.2 used 32â€bit integers - to represent bit positions in compressed files, so they - could not handle compressed files more than 512 megabytes - long. Versions 1.0.2 and above use 64â€bit ints on some - platforms which support them (GNU supported targets, and - Windows). To establish whether or not bzip2recover was - built with such a limitation, run it without arguments. - In any event you can build yourself an unlimited version - if you can recompile it with MaybeUInt64 set to be an - unsigned 64â€bit integer. - - - - -AAUUTTHHOORR - Julian Seward, jsewardbzip.org. - - http://www.bzip.org - - The ideas embodied in _b_z_i_p_2 are due to (at least) the fol­ - lowing people: Michael Burrows and David Wheeler (for the - block sorting transformation), David Wheeler (again, for - the Huffman coder), Peter Fenwick (for the structured cod­ - ing model in the original _b_z_i_p_, and many refinements), and - Alistair Moffat, Radford Neal and Ian Witten (for the - arithmetic coder in the original _b_z_i_p_)_. I am much - indebted for their help, support and advice. See the man­ - ual in the source distribution for pointers to sources of - documentation. Christian von Roques encouraged me to look - for faster sorting algorithms, so as to speed up compres­ - sion. Bela Lubkin encouraged me to improve the worstâ€case - compression performance. Donna Robinson XMLised the docu­ - mentation. The bz* scripts are derived from those of GNU - gzip. Many people sent patches, helped with portability - problems, lent machines, gave advice and were generally - helpful. - - - - bzip2(1) diff --git a/Utilities/cmbzip2/bzip2.c b/Utilities/cmbzip2/bzip2.c index 88e5f0936aa..d95d280619a 100644 --- a/Utilities/cmbzip2/bzip2.c +++ b/Utilities/cmbzip2/bzip2.c @@ -7,8 +7,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. @@ -128,12 +128,12 @@ #if BZ_LCCWIN32 # include # include -# include +# include # define NORETURN /**/ # define PATH_SEP '\\' -# define MY_LSTAT _stat -# define MY_STAT _stat +# define MY_LSTAT _stati64 +# define MY_STAT _stati64 # define MY_S_ISREG(x) ((x) & _S_IFREG) # define MY_S_ISDIR(x) ((x) & _S_IFDIR) @@ -380,27 +380,27 @@ void compressStream ( FILE *stream, FILE *zStream ) if (verbosity >= 1) { if (nbytes_in_lo32 == 0 && nbytes_in_hi32 == 0) { - fprintf ( stderr, " no data compressed.\n"); + fprintf ( stderr, " no data compressed.\n"); } else { - Char buf_nin[32], buf_nout[32]; - UInt64 nbytes_in, nbytes_out; - double nbytes_in_d, nbytes_out_d; - uInt64_from_UInt32s ( &nbytes_in, - nbytes_in_lo32, nbytes_in_hi32 ); - uInt64_from_UInt32s ( &nbytes_out, - nbytes_out_lo32, nbytes_out_hi32 ); - nbytes_in_d = uInt64_to_double ( &nbytes_in ); - nbytes_out_d = uInt64_to_double ( &nbytes_out ); - uInt64_toAscii ( buf_nin, &nbytes_in ); - uInt64_toAscii ( buf_nout, &nbytes_out ); - fprintf ( stderr, "%6.3f:1, %6.3f bits/byte, " - "%5.2f%% saved, %s in, %s out.\n", - nbytes_in_d / nbytes_out_d, - (8.0 * nbytes_out_d) / nbytes_in_d, - 100.0 * (1.0 - nbytes_out_d / nbytes_in_d), - buf_nin, - buf_nout - ); + Char buf_nin[32], buf_nout[32]; + UInt64 nbytes_in, nbytes_out; + double nbytes_in_d, nbytes_out_d; + uInt64_from_UInt32s ( &nbytes_in, + nbytes_in_lo32, nbytes_in_hi32 ); + uInt64_from_UInt32s ( &nbytes_out, + nbytes_out_lo32, nbytes_out_hi32 ); + nbytes_in_d = uInt64_to_double ( &nbytes_in ); + nbytes_out_d = uInt64_to_double ( &nbytes_out ); + uInt64_toAscii ( buf_nin, &nbytes_in ); + uInt64_toAscii ( buf_nout, &nbytes_out ); + fprintf ( stderr, "%6.3f:1, %6.3f bits/byte, " + "%5.2f%% saved, %s in, %s out.\n", + nbytes_in_d / nbytes_out_d, + (8.0 * nbytes_out_d) / nbytes_in_d, + 100.0 * (1.0 - nbytes_out_d / nbytes_in_d), + buf_nin, + buf_nout + ); } } @@ -505,11 +505,11 @@ Bool uncompressStream ( FILE *zStream, FILE *stream ) if (forceOverwrite) { rewind(zStream); while (True) { - if (myfeof(zStream)) break; - nread = fread ( obuf, sizeof(UChar), 5000, zStream ); - if (ferror(zStream)) goto errhandler_io; - if (nread > 0) fwrite ( obuf, sizeof(UChar), nread, stream ); - if (ferror(stream)) goto errhandler_io; + if (myfeof(zStream)) break; + nread = fread ( obuf, sizeof(UChar), 5000, zStream ); + if (ferror(zStream)) goto errhandler_io; + if (nread > 0) fwrite ( obuf, sizeof(UChar), nread, stream ); + if (ferror(stream)) goto errhandler_io; } goto closeok; } @@ -554,7 +554,7 @@ static Bool testStream ( FILE *zStream ) { BZFILE* bzf = NULL; - Int32 bzerr, bzerr_dummy, ret, nread, streamNo, i; + Int32 bzerr, bzerr_dummy, ret, streamNo, i; UChar obuf[5000]; UChar unused[BZ_MAX_UNUSED]; Int32 nUnused; @@ -577,7 +577,7 @@ Bool testStream ( FILE *zStream ) streamNo++; while (bzerr == BZ_OK) { - nread = BZ2_bzRead ( &bzerr, bzf, obuf, 5000 ); + BZ2_bzRead ( &bzerr, bzf, obuf, 5000 ); if (bzerr == BZ_DATA_ERROR_MAGIC) goto errhandler; } if (bzerr != BZ_STREAM_END) goto errhandler; @@ -748,8 +748,8 @@ void panic ( const Char* s ) fprintf ( stderr, "\n%s: PANIC -- internal consistency error:\n" "\t%s\n" - "\tThis is a BUG. Please report it to me at:\n" - "\tjseward@bzip.org\n", + "\tThis is a BUG. Please report it to:\n" + "\tbzip2-devel@sourceware.org\n", progName, s ); showFileNames(); cleanUpAndFail( 3 ); @@ -775,9 +775,9 @@ void compressedStreamEOF ( void ) { if (noisy) { fprintf ( stderr, - "\n%s: Compressed file ends unexpectedly;\n\t" - "perhaps it is corrupted? *Possible* reason follows.\n", - progName ); + "\n%s: Compressed file ends unexpectedly;\n\t" + "perhaps it is corrupted? *Possible* reason follows.\n", + progName ); perror ( progName ); showFileNames(); cadvise(); @@ -829,7 +829,7 @@ void mySIGSEGVorSIGBUScatcher ( IntNative n ) " The user's manual, Section 4.3, has more info on (1) and (2).\n" " \n" " If you suspect this is a bug in bzip2, or are unsure about (1)\n" - " or (2), feel free to report it to me at: jseward@bzip.org.\n" + " or (2), feel free to report it to: bzip2-devel@sourceware.org.\n" " Section 4.3 of the user's manual describes the info a useful\n" " bug report should have. If the manual is available on your\n" " system, please try and read it before mailing me. If you don't\n" @@ -852,7 +852,7 @@ void mySIGSEGVorSIGBUScatcher ( IntNative n ) " The user's manual, Section 4.3, has more info on (2) and (3).\n" " \n" " If you suspect this is a bug in bzip2, or are unsure about (2)\n" - " or (3), feel free to report it to me at: jseward@bzip.org.\n" + " or (3), feel free to report it to: bzip2-devel@sourceware.org.\n" " Section 4.3 of the user's manual describes the info a useful\n" " bug report should have. If the manual is available on your\n" " system, please try and read it before mailing me. If you don't\n" @@ -1200,12 +1200,12 @@ void compress ( Char *name ) } if ( srcMode == SM_F2F && fileExists ( outName ) ) { if (forceOverwrite) { - remove(outName); + remove(outName); } else { - fprintf ( stderr, "%s: Output file %s already exists.\n", - progName, outName ); - setExit(1); - return; + fprintf ( stderr, "%s: Output file %s already exists.\n", + progName, outName ); + setExit(1); + return; } } if ( srcMode == SM_F2F && !forceOverwrite && @@ -1386,7 +1386,7 @@ void uncompress ( Char *name ) } if ( srcMode == SM_F2F && fileExists ( outName ) ) { if (forceOverwrite) { - remove(outName); + remove(outName); } else { fprintf ( stderr, "%s: Output file %s already exists.\n", progName, outName ); @@ -1605,11 +1605,11 @@ void license ( void ) "bzip2, a block-sorting file compressor. " "Version %s.\n" " \n" - " Copyright (C) 1996-2007 by Julian Seward.\n" + " Copyright (C) 1996-2019 by Julian Seward.\n" " \n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms set out in the LICENSE file, which is included\n" - " in the bzip2-1.0.5 source distribution.\n" + " in the bzip2 source distribution.\n" " \n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" @@ -1997,18 +1997,20 @@ IntNative main ( IntNative argc, Char *argv[] ) } else { decode = True; for (aa = argList; aa != NULL; aa = aa->link) { - if (ISFLAG("--")) { decode = False; continue; } + if (ISFLAG("--")) { decode = False; continue; } if (aa->name[0] == '-' && decode) continue; numFilesProcessed++; testf ( aa->name ); - } + } } - if (testFailsExist && noisy) { - fprintf ( stderr, - "\n" - "You can use the `bzip2recover' program to attempt to recover\n" - "data from undamaged sections of corrupted files.\n\n" - ); + if (testFailsExist) { + if (noisy) { + fprintf ( stderr, + "\n" + "You can use the `bzip2recover' program to attempt to recover\n" + "data from undamaged sections of corrupted files.\n\n" + ); + } setExit(2); exit(exitValue); } diff --git a/Utilities/cmbzip2/bzip2.txt b/Utilities/cmbzip2/bzip2.txt deleted file mode 100644 index 4fb9c7435ee..00000000000 --- a/Utilities/cmbzip2/bzip2.txt +++ /dev/null @@ -1,391 +0,0 @@ - -NAME - bzip2, bunzip2 - a block-sorting file compressor, v1.0.4 - bzcat - decompresses files to stdout - bzip2recover - recovers data from damaged bzip2 files - - -SYNOPSIS - bzip2 [ -cdfkqstvzVL123456789 ] [ filenames ... ] - bunzip2 [ -fkvsVL ] [ filenames ... ] - bzcat [ -s ] [ filenames ... ] - bzip2recover filename - - -DESCRIPTION - bzip2 compresses files using the Burrows-Wheeler block - sorting text compression algorithm, and Huffman coding. - Compression is generally considerably better than that - achieved by more conventional LZ77/LZ78-based compressors, - and approaches the performance of the PPM family of sta- - tistical compressors. - - The command-line options are deliberately very similar to - those of GNU gzip, but they are not identical. - - bzip2 expects a list of file names to accompany the com- - mand-line flags. Each file is replaced by a compressed - version of itself, with the name "original_name.bz2". - Each compressed file has the same modification date, per- - missions, and, when possible, ownership as the correspond- - ing original, so that these properties can be correctly - restored at decompression time. File name handling is - naive in the sense that there is no mechanism for preserv- - ing original file names, permissions, ownerships or dates - in filesystems which lack these concepts, or have serious - file name length restrictions, such as MS-DOS. - - bzip2 and bunzip2 will by default not overwrite existing - files. If you want this to happen, specify the -f flag. - - If no file names are specified, bzip2 compresses from - standard input to standard output. In this case, bzip2 - will decline to write compressed output to a terminal, as - this would be entirely incomprehensible and therefore - pointless. - - bunzip2 (or bzip2 -d) decompresses all specified files. - Files which were not created by bzip2 will be detected and - ignored, and a warning issued. bzip2 attempts to guess - the filename for the decompressed file from that of the - compressed file as follows: - - filename.bz2 becomes filename - filename.bz becomes filename - filename.tbz2 becomes filename.tar - filename.tbz becomes filename.tar - anyothername becomes anyothername.out - - If the file does not end in one of the recognised endings, - .bz2, .bz, .tbz2 or .tbz, bzip2 complains that it cannot - guess the name of the original file, and uses the original - name with .out appended. - - As with compression, supplying no filenames causes decom- - pression from standard input to standard output. - - bunzip2 will correctly decompress a file which is the con- - catenation of two or more compressed files. The result is - the concatenation of the corresponding uncompressed files. - Integrity testing (-t) of concatenated compressed files is - also supported. - - You can also compress or decompress files to the standard - output by giving the -c flag. Multiple files may be com- - pressed and decompressed like this. The resulting outputs - are fed sequentially to stdout. Compression of multiple - files in this manner generates a stream containing multi- - ple compressed file representations. Such a stream can be - decompressed correctly only by bzip2 version 0.9.0 or - later. Earlier versions of bzip2 will stop after decom- - pressing the first file in the stream. - - bzcat (or bzip2 -dc) decompresses all specified files to - the standard output. - - bzip2 will read arguments from the environment variables - BZIP2 and BZIP, in that order, and will process them - before any arguments read from the command line. This - gives a convenient way to supply default arguments. - - Compression is always performed, even if the compressed - file is slightly larger than the original. Files of less - than about one hundred bytes tend to get larger, since the - compression mechanism has a constant overhead in the - region of 50 bytes. Random data (including the output of - most file compressors) is coded at about 8.05 bits per - byte, giving an expansion of around 0.5%. - - As a self-check for your protection, bzip2 uses 32-bit - CRCs to make sure that the decompressed version of a file - is identical to the original. This guards against corrup- - tion of the compressed data, and against undetected bugs - in bzip2 (hopefully very unlikely). The chances of data - corruption going undetected is microscopic, about one - chance in four billion for each file processed. Be aware, - though, that the check occurs upon decompression, so it - can only tell you that something is wrong. It can't help - you recover the original uncompressed data. You can use - bzip2recover to try to recover data from damaged files. - - Return values: 0 for a normal exit, 1 for environmental - problems (file not found, invalid flags, I/O errors, &c), - 2 to indicate a corrupt compressed file, 3 for an internal - consistency error (eg, bug) which caused bzip2 to panic. - - -OPTIONS - -c --stdout - Compress or decompress to standard output. - - -d --decompress - Force decompression. bzip2, bunzip2 and bzcat are - really the same program, and the decision about - what actions to take is done on the basis of which - name is used. This flag overrides that mechanism, - and forces bzip2 to decompress. - - -z --compress - The complement to -d: forces compression, - regardless of the invocation name. - - -t --test - Check integrity of the specified file(s), but don't - decompress them. This really performs a trial - decompression and throws away the result. - - -f --force - Force overwrite of output files. Normally, bzip2 - will not overwrite existing output files. Also - forces bzip2 to break hard links to files, which it - otherwise wouldn't do. - - bzip2 normally declines to decompress files which - don't have the correct magic header bytes. If - forced (-f), however, it will pass such files - through unmodified. This is how GNU gzip behaves. - - -k --keep - Keep (don't delete) input files during compression - or decompression. - - -s --small - Reduce memory usage, for compression, decompression - and testing. Files are decompressed and tested - using a modified algorithm which only requires 2.5 - bytes per block byte. This means any file can be - decompressed in 2300k of memory, albeit at about - half the normal speed. - - During compression, -s selects a block size of - 200k, which limits memory use to around the same - figure, at the expense of your compression ratio. - In short, if your machine is low on memory (8 - megabytes or less), use -s for everything. See - MEMORY MANAGEMENT below. - - -q --quiet - Suppress non-essential warning messages. Messages - pertaining to I/O errors and other critical events - will not be suppressed. - - -v --verbose - Verbose mode -- show the compression ratio for each - file processed. Further -v's increase the ver- - bosity level, spewing out lots of information which - is primarily of interest for diagnostic purposes. - - -L --license -V --version - Display the software version, license terms and - conditions. - - -1 (or --fast) to -9 (or --best) - Set the block size to 100 k, 200 k .. 900 k when - compressing. Has no effect when decompressing. - See MEMORY MANAGEMENT below. The --fast and --best - aliases are primarily for GNU gzip compatibility. - In particular, --fast doesn't make things signifi- - cantly faster. And --best merely selects the - default behaviour. - - -- Treats all subsequent arguments as file names, even - if they start with a dash. This is so you can han- - dle files with names beginning with a dash, for - example: bzip2 -- -myfilename. - - --repetitive-fast --repetitive-best - These flags are redundant in versions 0.9.5 and - above. They provided some coarse control over the - behaviour of the sorting algorithm in earlier ver- - sions, which was sometimes useful. 0.9.5 and above - have an improved algorithm which renders these - flags irrelevant. - - -MEMORY MANAGEMENT - bzip2 compresses large files in blocks. The block size - affects both the compression ratio achieved, and the - amount of memory needed for compression and decompression. - The flags -1 through -9 specify the block size to be - 100,000 bytes through 900,000 bytes (the default) respec- - tively. At decompression time, the block size used for - compression is read from the header of the compressed - file, and bunzip2 then allocates itself just enough memory - to decompress the file. Since block sizes are stored in - compressed files, it follows that the flags -1 to -9 are - irrelevant to and so ignored during decompression. - - Compression and decompression requirements, in bytes, can - be estimated as: - - Compression: 400k + ( 8 x block size ) - - Decompression: 100k + ( 4 x block size ), or - 100k + ( 2.5 x block size ) - - Larger block sizes give rapidly diminishing marginal - returns. Most of the compression comes from the first two - or three hundred k of block size, a fact worth bearing in - mind when using bzip2 on small machines. It is also - important to appreciate that the decompression memory - requirement is set at compression time by the choice of - block size. - - For files compressed with the default 900k block size, - bunzip2 will require about 3700 kbytes to decompress. To - support decompression of any file on a 4 megabyte machine, - bunzip2 has an option to decompress using approximately - half this amount of memory, about 2300 kbytes. Decompres- - sion speed is also halved, so you should use this option - only where necessary. The relevant flag is -s. - - In general, try and use the largest block size memory con- - straints allow, since that maximises the compression - achieved. Compression and decompression speed are virtu- - ally unaffected by block size. - - Another significant point applies to files which fit in a - single block -- that means most files you'd encounter - using a large block size. The amount of real memory - touched is proportional to the size of the file, since the - file is smaller than a block. For example, compressing a - file 20,000 bytes long with the flag -9 will cause the - compressor to allocate around 7600k of memory, but only - touch 400k + 20000 * 8 = 560 kbytes of it. Similarly, the - decompressor will allocate 3700k but only touch 100k + - 20000 * 4 = 180 kbytes. - - Here is a table which summarises the maximum memory usage - for different block sizes. Also recorded is the total - compressed size for 14 files of the Calgary Text Compres- - sion Corpus totalling 3,141,622 bytes. This column gives - some feel for how compression varies with block size. - These figures tend to understate the advantage of larger - block sizes for larger files, since the Corpus is domi- - nated by smaller files. - - Compress Decompress Decompress Corpus - Flag usage usage -s usage Size - - -1 1200k 500k 350k 914704 - -2 2000k 900k 600k 877703 - -3 2800k 1300k 850k 860338 - -4 3600k 1700k 1100k 846899 - -5 4400k 2100k 1350k 845160 - -6 5200k 2500k 1600k 838626 - -7 6100k 2900k 1850k 834096 - -8 6800k 3300k 2100k 828642 - -9 7600k 3700k 2350k 828642 - - -RECOVERING DATA FROM DAMAGED FILES - bzip2 compresses files in blocks, usually 900kbytes long. - Each block is handled independently. If a media or trans- - mission error causes a multi-block .bz2 file to become - damaged, it may be possible to recover data from the - undamaged blocks in the file. - - The compressed representation of each block is delimited - by a 48-bit pattern, which makes it possible to find the - block boundaries with reasonable certainty. Each block - also carries its own 32-bit CRC, so damaged blocks can be - distinguished from undamaged ones. - - bzip2recover is a simple program whose purpose is to - search for blocks in .bz2 files, and write each block out - into its own .bz2 file. You can then use bzip2 -t to test - the integrity of the resulting files, and decompress those - which are undamaged. - - bzip2recover takes a single argument, the name of the dam- - aged file, and writes a number of files - "rec00001file.bz2", "rec00002file.bz2", etc, containing - the extracted blocks. The output filenames are - designed so that the use of wildcards in subsequent pro- - cessing -- for example, "bzip2 -dc rec*file.bz2 > recov- - ered_data" -- processes the files in the correct order. - - bzip2recover should be of most use dealing with large .bz2 - files, as these will contain many blocks. It is clearly - futile to use it on damaged single-block files, since a - damaged block cannot be recovered. If you wish to min- - imise any potential data loss through media or transmis- - sion errors, you might consider compressing with a smaller - block size. - - -PERFORMANCE NOTES - The sorting phase of compression gathers together similar - strings in the file. Because of this, files containing - very long runs of repeated symbols, like "aabaabaabaab - ..." (repeated several hundred times) may compress more - slowly than normal. Versions 0.9.5 and above fare much - better than previous versions in this respect. The ratio - between worst-case and average-case compression time is in - the region of 10:1. For previous versions, this figure - was more like 100:1. You can use the -vvvv option to mon- - itor progress in great detail, if you want. - - Decompression speed is unaffected by these phenomena. - - bzip2 usually allocates several megabytes of memory to - operate in, and then charges all over it in a fairly ran- - dom fashion. This means that performance, both for com- - pressing and decompressing, is largely determined by the - speed at which your machine can service cache misses. - Because of this, small changes to the code to reduce the - miss rate have been observed to give disproportionately - large performance improvements. I imagine bzip2 will per- - form best on machines with very large caches. - - -CAVEATS - I/O error messages are not as helpful as they could be. - bzip2 tries hard to detect I/O errors and exit cleanly, - but the details of what the problem is sometimes seem - rather misleading. - - This manual page pertains to version 1.0.4 of bzip2. Com- - pressed data created by this version is entirely forwards - and backwards compatible with the previous public - releases, versions 0.1pl2, 0.9.0, 0.9.5, 1.0.0, 1.0.1, - 1.0.2 and 1.0.3, but with the following exception: 0.9.0 - and above can correctly decompress multiple concatenated - compressed files. 0.1pl2 cannot do this; it will stop - after decompressing just the first file in the stream. - - bzip2recover versions prior to 1.0.2 used 32-bit integers - to represent bit positions in compressed files, so they - could not handle compressed files more than 512 megabytes - long. Versions 1.0.2 and above use 64-bit ints on some - platforms which support them (GNU supported targets, and - Windows). To establish whether or not bzip2recover was - built with such a limitation, run it without arguments. - In any event you can build yourself an unlimited version - if you can recompile it with MaybeUInt64 set to be an - unsigned 64-bit integer. - - -AUTHOR - Julian Seward, jsewardbzip.org. - - http://www.bzip.org - - The ideas embodied in bzip2 are due to (at least) the fol- - lowing people: Michael Burrows and David Wheeler (for the - block sorting transformation), David Wheeler (again, for - the Huffman coder), Peter Fenwick (for the structured cod- - ing model in the original bzip, and many refinements), and - Alistair Moffat, Radford Neal and Ian Witten (for the - arithmetic coder in the original bzip). I am much - indebted for their help, support and advice. See the man- - ual in the source distribution for pointers to sources of - documentation. Christian von Roques encouraged me to look - for faster sorting algorithms, so as to speed up compres- - sion. Bela Lubkin encouraged me to improve the worst-case - compression performance. Donna Robinson XMLised the docu- - mentation. The bz* scripts are derived from those of GNU - gzip. Many people sent patches, helped with portability - problems, lent machines, gave advice and were generally - helpful. - diff --git a/Utilities/cmbzip2/bzip2recover.c b/Utilities/cmbzip2/bzip2recover.c index 6e47b603b15..a8131e0611e 100644 --- a/Utilities/cmbzip2/bzip2recover.c +++ b/Utilities/cmbzip2/bzip2recover.c @@ -7,8 +7,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. @@ -18,7 +18,7 @@ ------------------------------------------------------------------ */ /* This program is a complete hack and should be rewritten properly. - It isn't very complicated. */ + It isn't very complicated. */ #include #include @@ -309,11 +309,12 @@ Int32 main ( Int32 argc, Char** argv ) UInt32 buffHi, buffLo, blockCRC; Char* p; - strcpy ( progName, argv[0] ); + strncpy ( progName, argv[0], BZ_MAX_FILENAME-1); + progName[BZ_MAX_FILENAME-1]='\0'; inFileName[0] = outFileName[0] = 0; fprintf ( stderr, - "bzip2recover 1.0.5: extracts blocks from damaged .bz2 files.\n" ); + "bzip2recover 1.0.8: extracts blocks from damaged .bz2 files.\n" ); if (argc != 2) { fprintf ( stderr, "%s: usage is `%s damaged_file_name'.\n", @@ -393,7 +394,7 @@ Int32 main ( Int32 argc, Char** argv ) bEnd[currBlock] = 0; } if (currBlock > 0 && - (bEnd[currBlock] - bStart[currBlock]) >= 130) { + (bEnd[currBlock] - bStart[currBlock]) >= 130) { fprintf ( stderr, " block %d runs from " MaybeUInt64_FMT " to " MaybeUInt64_FMT "\n", rbCtr+1, bStart[currBlock], bEnd[currBlock] ); @@ -457,6 +458,7 @@ Int32 main ( Int32 argc, Char** argv ) bsPutUChar ( bsWr, 0x50 ); bsPutUChar ( bsWr, 0x90 ); bsPutUInt32 ( bsWr, blockCRC ); bsClose ( bsWr ); + outFile = NULL; } if (wrBlock >= rbCtr) break; wrBlock++; @@ -474,8 +476,8 @@ Int32 main ( Int32 argc, Char** argv ) split = outFileName; } else { ++split; - } - /* Now split points to the start of the basename. */ + } + /* Now split points to the start of the basename. */ ofs = split - outFileName; sprintf (split, "rec%5d", wrBlock+1); for (p = split; *p != 0; p++) if (*p == ' ') *p = '0'; diff --git a/Utilities/cmbzip2/bzlib.c b/Utilities/cmbzip2/bzlib.c index aeecef10f74..21786551b60 100644 --- a/Utilities/cmbzip2/bzlib.c +++ b/Utilities/cmbzip2/bzlib.c @@ -8,8 +8,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. @@ -43,12 +43,12 @@ void BZ2_bz__AssertH__fail ( int errcode ) fprintf(stderr, "\n\nbzip2/libbzip2: internal error number %d.\n" "This is a bug in bzip2/libbzip2, %s.\n" - "Please report it to me at: jseward@bzip.org. If this happened\n" + "Please report it to: bzip2-devel@sourceware.org. If this happened\n" "when you were using some program which uses libbzip2 as a\n" "component, you should also report this bug to the author(s)\n" "of that program. Please make an effort to report this bug;\n" "timely and accurate bug reports eventually lead to higher\n" - "quality software. Thanks. Julian Seward, 10 December 2007.\n\n", + "quality software. Thanks.\n\n", errcode, BZ2_bzlibVersion() ); @@ -102,7 +102,6 @@ static void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) { void* v = malloc ( items * size ); - (void)opaque; return v; } @@ -110,7 +109,6 @@ static void default_bzfree ( void* opaque, void* addr ) { if (addr != NULL) free ( addr ); - (void)opaque; } @@ -427,7 +425,7 @@ int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action ) return progress ? BZ_RUN_OK : BZ_PARAM_ERROR; } else - if (action == BZ_FLUSH) { + if (action == BZ_FLUSH) { s->avail_in_expect = strm->avail_in; s->mode = BZ_M_FLUSHING; goto preswitch; @@ -1507,7 +1505,6 @@ int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len ) /*---------------------------------------------------*/ int BZ_API(BZ2_bzflush) (BZFILE *b) { - (void) b; /* do nothing now... */ return 0; } diff --git a/Utilities/cmbzip2/bzlib.h b/Utilities/cmbzip2/bzlib.h index c5b75d6d8ff..8966a6c5804 100644 --- a/Utilities/cmbzip2/bzlib.h +++ b/Utilities/cmbzip2/bzlib.h @@ -8,8 +8,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. diff --git a/Utilities/cmbzip2/bzlib_private.h b/Utilities/cmbzip2/bzlib_private.h index 02a667f499d..ba0f58986ca 100644 --- a/Utilities/cmbzip2/bzlib_private.h +++ b/Utilities/cmbzip2/bzlib_private.h @@ -8,8 +8,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. @@ -32,28 +32,13 @@ #include "bzlib.h" - -#if defined(__BORLANDC__) -# pragma warn -8004 /* Assigned value never used. */ -# pragma warn -8008 /* Condition is always true/false. */ -# pragma warn -8066 /* Unreachable code. */ -# pragma warn -8057 /* Unused parameter. */ -#endif #if defined(_MSC_VER) -/* 'integral size mismatch in argument; conversion supplied */ -# pragma warning(disable:4244) -/* conversion from 'size_t' to 'off_t', possible loss of data */ -# pragma warning(disable:4267) -/* warning C4127: conditional expression is constant*/ -# pragma warning(disable:4127) -#endif -#if defined(__clang__) -# pragma clang diagnostic ignored "-Wcast-align" +# pragma warning(push,1) #endif /*-- General stuff. --*/ -#define BZ_VERSION "1.0.5, 10-Dec-2007" +#define BZ_VERSION "1.0.8, 13-Jul-2019" typedef char Char; typedef unsigned char Bool; diff --git a/Utilities/cmbzip2/bzmore b/Utilities/cmbzip2/bzmore deleted file mode 100644 index 21b1de61c5e..00000000000 --- a/Utilities/cmbzip2/bzmore +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/sh - -# Bzmore wrapped for bzip2, -# adapted from zmore by Philippe Troin for Debian GNU/Linux. - -PATH="/usr/bin:$PATH"; export PATH - -prog=`echo $0 | sed 's|.*/||'` -case "$prog" in - *less) more=less ;; - *) more=more ;; -esac - -if test "`echo -n a`" = "-n a"; then - # looks like a SysV system: - n1=''; n2='\c' -else - n1='-n'; n2='' -fi -oldtty=`stty -g 2>/dev/null` -if stty -cbreak 2>/dev/null; then - cb='cbreak'; ncb='-cbreak' -else - # 'stty min 1' resets eof to ^a on both SunOS and SysV! - cb='min 1 -icanon'; ncb='icanon eof ^d' -fi -if test $? -eq 0 -a -n "$oldtty"; then - trap 'stty $oldtty 2>/dev/null; exit' 0 2 3 5 10 13 15 -else - trap 'stty $ncb echo 2>/dev/null; exit' 0 2 3 5 10 13 15 -fi - -if test $# = 0; then - if test -t 0; then - echo usage: $prog files... - else - bzip2 -cdfq | eval $more - fi -else - FIRST=1 - for FILE - do - if test $FIRST -eq 0; then - echo $n1 "--More--(Next file: $FILE)$n2" - stty $cb -echo 2>/dev/null - ANS=`dd bs=1 count=1 2>/dev/null` - stty $ncb echo 2>/dev/null - echo " " - if test "$ANS" = 'e' -o "$ANS" = 'q'; then - exit - fi - fi - if test "$ANS" != 's'; then - echo "------> $FILE <------" - bzip2 -cdfq "$FILE" | eval $more - fi - if test -t; then - FIRST=0 - fi - done -fi diff --git a/Utilities/cmbzip2/bzmore.1 b/Utilities/cmbzip2/bzmore.1 deleted file mode 100644 index c6868ed5cfc..00000000000 --- a/Utilities/cmbzip2/bzmore.1 +++ /dev/null @@ -1,152 +0,0 @@ -.\"Shamelessly copied from zmore.1 by Philippe Troin -.\"for Debian GNU/Linux -.TH BZMORE 1 -.SH NAME -bzmore, bzless \- file perusal filter for crt viewing of bzip2 compressed text -.SH SYNOPSIS -.B bzmore -[ name ... ] -.br -.B bzless -[ name ... ] -.SH NOTE -In the following description, -.I bzless -and -.I less -can be used interchangeably with -.I bzmore -and -.I more. -.SH DESCRIPTION -.I Bzmore -is a filter which allows examination of compressed or plain text files -one screenful at a time on a soft-copy terminal. -.I bzmore -works on files compressed with -.I bzip2 -and also on uncompressed files. -If a file does not exist, -.I bzmore -looks for a file of the same name with the addition of a .bz2 suffix. -.PP -.I Bzmore -normally pauses after each screenful, printing --More-- -at the bottom of the screen. -If the user then types a carriage return, one more line is displayed. -If the user hits a space, -another screenful is displayed. Other possibilities are enumerated later. -.PP -.I Bzmore -looks in the file -.I /etc/termcap -to determine terminal characteristics, -and to determine the default window size. -On a terminal capable of displaying 24 lines, -the default window size is 22 lines. -Other sequences which may be typed when -.I bzmore -pauses, and their effects, are as follows (\fIi\fP is an optional integer -argument, defaulting to 1) : -.PP -.IP \fIi\|\fP -display -.I i -more lines, (or another screenful if no argument is given) -.PP -.IP ^D -display 11 more lines (a ``scroll''). -If -.I i -is given, then the scroll size is set to \fIi\|\fP. -.PP -.IP d -same as ^D (control-D) -.PP -.IP \fIi\|\fPz -same as typing a space except that \fIi\|\fP, if present, becomes the new -window size. Note that the window size reverts back to the default at the -end of the current file. -.PP -.IP \fIi\|\fPs -skip \fIi\|\fP lines and print a screenful of lines -.PP -.IP \fIi\|\fPf -skip \fIi\fP screenfuls and print a screenful of lines -.PP -.IP "q or Q" -quit reading the current file; go on to the next (if any) -.PP -.IP "e or q" -When the prompt --More--(Next file: -.IR file ) -is printed, this command causes bzmore to exit. -.PP -.IP s -When the prompt --More--(Next file: -.IR file ) -is printed, this command causes bzmore to skip the next file and continue. -.PP -.IP = -Display the current line number. -.PP -.IP \fIi\|\fP/expr -search for the \fIi\|\fP-th occurrence of the regular expression \fIexpr.\fP -If the pattern is not found, -.I bzmore -goes on to the next file (if any). -Otherwise, a screenful is displayed, starting two lines before the place -where the expression was found. -The user's erase and kill characters may be used to edit the regular -expression. -Erasing back past the first column cancels the search command. -.PP -.IP \fIi\|\fPn -search for the \fIi\|\fP-th occurrence of the last regular expression entered. -.PP -.IP !command -invoke a shell with \fIcommand\|\fP. -The character `!' in "command" are replaced with the -previous shell command. The sequence "\\!" is replaced by "!". -.PP -.IP ":q or :Q" -quit reading the current file; go on to the next (if any) -(same as q or Q). -.PP -.IP . -(dot) repeat the previous command. -.PP -The commands take effect immediately, i.e., it is not necessary to -type a carriage return. -Up to the time when the command character itself is given, -the user may hit the line kill character to cancel the numerical -argument being formed. -In addition, the user may hit the erase character to redisplay the ---More-- message. -.PP -At any time when output is being sent to the terminal, the user can -hit the quit key (normally control\-\\). -.I Bzmore -will stop sending output, and will display the usual --More-- -prompt. -The user may then enter one of the above commands in the normal manner. -Unfortunately, some output is lost when this is done, due to the -fact that any characters waiting in the terminal's output queue -are flushed when the quit signal occurs. -.PP -The terminal is set to -.I noecho -mode by this program so that the output can be continuous. -What you type will thus not show on your terminal, except for the / and ! -commands. -.PP -If the standard output is not a teletype, then -.I bzmore -acts just like -.I bzcat, -except that a header is printed before each file. -.SH FILES -.DT -/etc/termcap Terminal data base -.SH "SEE ALSO" -more(1), less(1), bzip2(1), bzdiff(1), bzgrep(1) diff --git a/Utilities/cmbzip2/compress.c b/Utilities/cmbzip2/compress.c index feea233c287..5dfa00231b0 100644 --- a/Utilities/cmbzip2/compress.c +++ b/Utilities/cmbzip2/compress.c @@ -8,8 +8,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. @@ -239,7 +239,7 @@ static void sendMTFValues ( EState* s ) { Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; - Int32 nSelectors = 0, alphaSize, minLen, maxLen, selCtr; + Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; Int32 nGroups, nBytes; /*-- @@ -329,14 +329,14 @@ void sendMTFValues ( EState* s ) /*--- Set up an auxiliary length table which is used to fast-track - the common case (nGroups == 6). + the common case (nGroups == 6). ---*/ if (nGroups == 6) { for (v = 0; v < alphaSize; v++) { s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; - } + } } nSelectors = 0; @@ -385,7 +385,7 @@ void sendMTFValues ( EState* s ) cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; } else { - /*--- slow version which correctly handles all situations ---*/ + /*--- slow version which correctly handles all situations ---*/ for (i = gs; i <= ge; i++) { UInt16 icv = mtfv[i]; for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; @@ -426,7 +426,7 @@ void sendMTFValues ( EState* s ) # undef BZ_ITUR } else { - /*--- slow version which correctly handles all situations ---*/ + /*--- slow version which correctly handles all situations ---*/ for (i = gs; i <= ge; i++) s->rfreq[bt][ mtfv[i] ]++; } @@ -454,7 +454,7 @@ void sendMTFValues ( EState* s ) AssertH( nGroups < 8, 3002 ); AssertH( nSelectors < 32768 && - nSelectors <= (2 + (900000 / BZ_G_SIZE)), + nSelectors <= BZ_MAX_SELECTORS, 3003 ); @@ -579,7 +579,7 @@ void sendMTFValues ( EState* s ) # undef BZ_ITAH } else { - /*--- slow version which correctly handles all situations ---*/ + /*--- slow version which correctly handles all situations ---*/ for (i = gs; i <= ge; i++) { bsW ( s, s->len [s->selector[selCtr]] [mtfv[i]], diff --git a/Utilities/cmbzip2/crctable.c b/Utilities/cmbzip2/crctable.c index 215687b2c05..2b33c253533 100644 --- a/Utilities/cmbzip2/crctable.c +++ b/Utilities/cmbzip2/crctable.c @@ -8,8 +8,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. diff --git a/Utilities/cmbzip2/decompress.c b/Utilities/cmbzip2/decompress.c index bba5e0fa36d..a1a0bac8922 100644 --- a/Utilities/cmbzip2/decompress.c +++ b/Utilities/cmbzip2/decompress.c @@ -8,8 +8,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. @@ -285,7 +285,7 @@ Int32 BZ2_decompress ( DState* s ) /*--- Now the selectors ---*/ GET_BITS(BZ_X_SELECTOR_1, nGroups, 3); - if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR); + if (nGroups < 2 || nGroups > BZ_N_GROUPS) RETURN(BZ_DATA_ERROR); GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15); if (nSelectors < 1) RETURN(BZ_DATA_ERROR); for (i = 0; i < nSelectors; i++) { @@ -296,8 +296,14 @@ Int32 BZ2_decompress ( DState* s ) j++; if (j >= nGroups) RETURN(BZ_DATA_ERROR); } - s->selectorMtf[i] = j; + /* Having more than BZ_MAX_SELECTORS doesn't make much sense + since they will never be used, but some implementations might + "round up" the number of selectors, so just ignore those. */ + if (i < BZ_MAX_SELECTORS) + s->selectorMtf[i] = j; } + if (nSelectors > BZ_MAX_SELECTORS) + nSelectors = BZ_MAX_SELECTORS; /*--- Undo the MTF values for the selectors. ---*/ { @@ -381,6 +387,13 @@ Int32 BZ2_decompress ( DState* s ) es = -1; N = 1; do { + /* Check that N doesn't get too big, so that es doesn't + go negative. The maximum value that can be + RUNA/RUNB encoded is equal to the block size (post + the initial RLE), viz, 900k, so bounding N at 2 + million should guard against overflow without + rejecting any legitimate inputs. */ + if (N >= 2*1024*1024) RETURN(BZ_DATA_ERROR); if (nextSym == BZ_RUNA) es = es + (0+1) * N; else if (nextSym == BZ_RUNB) es = es + (1+1) * N; N = N * 2; @@ -485,15 +498,28 @@ Int32 BZ2_decompress ( DState* s ) RETURN(BZ_DATA_ERROR); /*-- Set up cftab to facilitate generation of T^(-1) --*/ + /* Check: unzftab entries in range. */ + for (i = 0; i <= 255; i++) { + if (s->unzftab[i] < 0 || s->unzftab[i] > nblock) + RETURN(BZ_DATA_ERROR); + } + /* Actually generate cftab. */ s->cftab[0] = 0; for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1]; for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1]; + /* Check: cftab entries in range. */ for (i = 0; i <= 256; i++) { if (s->cftab[i] < 0 || s->cftab[i] > nblock) { /* s->cftab[i] can legitimately be == nblock */ RETURN(BZ_DATA_ERROR); } } + /* Check: cftab entries non-descending. */ + for (i = 1; i <= 256; i++) { + if (s->cftab[i-1] > s->cftab[i]) { + RETURN(BZ_DATA_ERROR); + } + } s->state_out_len = 0; s->state_out_ch = 0; diff --git a/Utilities/cmbzip2/dlltest.c b/Utilities/cmbzip2/dlltest.c index 4e27da2800d..03fa14620e5 100644 --- a/Utilities/cmbzip2/dlltest.c +++ b/Utilities/cmbzip2/dlltest.c @@ -1,175 +1,175 @@ -/* - minibz2 - libbz2.dll test program. - by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) - This file is Public Domain. Welcome any email to me. - - usage: minibz2 [-d] [-{1,2,..9}] [[srcfilename] destfilename] -*/ - -#define BZ_IMPORT -#include -#include -#include "bzlib.h" -#ifdef _WIN32 -#include -#endif - - -#ifdef _WIN32 - -#define BZ2_LIBNAME "libbz2-1.0.2.DLL" - -#include -static int BZ2DLLLoaded = 0; -static HINSTANCE BZ2DLLhLib; -int BZ2DLLLoadLibrary(void) -{ - HINSTANCE hLib; - - if(BZ2DLLLoaded==1){return 0;} - hLib=LoadLibrary(BZ2_LIBNAME); - if(hLib == NULL){ - fprintf(stderr,"Can't load %s\n",BZ2_LIBNAME); - return -1; - } - BZ2_bzlibVersion=GetProcAddress(hLib,"BZ2_bzlibVersion"); - BZ2_bzopen=GetProcAddress(hLib,"BZ2_bzopen"); - BZ2_bzdopen=GetProcAddress(hLib,"BZ2_bzdopen"); - BZ2_bzread=GetProcAddress(hLib,"BZ2_bzread"); - BZ2_bzwrite=GetProcAddress(hLib,"BZ2_bzwrite"); - BZ2_bzflush=GetProcAddress(hLib,"BZ2_bzflush"); - BZ2_bzclose=GetProcAddress(hLib,"BZ2_bzclose"); - BZ2_bzerror=GetProcAddress(hLib,"BZ2_bzerror"); - - if (!BZ2_bzlibVersion || !BZ2_bzopen || !BZ2_bzdopen - || !BZ2_bzread || !BZ2_bzwrite || !BZ2_bzflush - || !BZ2_bzclose || !BZ2_bzerror) { - fprintf(stderr,"GetProcAddress failed.\n"); - return -1; - } - BZ2DLLLoaded=1; - BZ2DLLhLib=hLib; - return 0; - -} -int BZ2DLLFreeLibrary(void) -{ - if(BZ2DLLLoaded==0){return 0;} - FreeLibrary(BZ2DLLhLib); - BZ2DLLLoaded=0; -} -#endif /* WIN32 */ - -void usage(void) -{ - puts("usage: minibz2 [-d] [-{1,2,..9}] [[srcfilename] destfilename]"); -} - -int main(int argc,char *argv[]) -{ - int decompress = 0; - int level = 9; - char *fn_r = NULL; - char *fn_w = NULL; - -#ifdef _WIN32 - if(BZ2DLLLoadLibrary()<0){ - fprintf(stderr,"Loading of %s failed. Giving up.\n", BZ2_LIBNAME); - exit(1); - } - printf("Loading of %s succeeded. Library version is %s.\n", - BZ2_LIBNAME, BZ2_bzlibVersion() ); -#endif - while(++argv,--argc){ - if(**argv =='-' || **argv=='/'){ - char *p; - - for(p=*argv+1;*p;p++){ - if(*p=='d'){ - decompress = 1; - }else if('1'<=*p && *p<='9'){ - level = *p - '0'; - }else{ - usage(); - exit(1); - } - } - }else{ - break; - } - } - if(argc>=1){ - fn_r = *argv; - argc--;argv++; - }else{ - fn_r = NULL; - } - if(argc>=1){ - fn_w = *argv; - argc--;argv++; - }else{ - fn_w = NULL; - } - { - int len; - char buff[0x1000]; - char mode[10]; - - if(decompress){ - BZFILE *BZ2fp_r = NULL; - FILE *fp_w = NULL; - - if(fn_w){ - if((fp_w = fopen(fn_w,"wb"))==NULL){ - printf("can't open [%s]\n",fn_w); - perror("reason:"); - exit(1); - } - }else{ - fp_w = stdout; - } - if((fn_r == NULL && (BZ2fp_r = BZ2_bzdopen(fileno(stdin),"rb"))==NULL) - || (fn_r != NULL && (BZ2fp_r = BZ2_bzopen(fn_r,"rb"))==NULL)){ - printf("can't bz2openstream\n"); - exit(1); - } - while((len=BZ2_bzread(BZ2fp_r,buff,0x1000))>0){ - fwrite(buff,1,len,fp_w); - } - BZ2_bzclose(BZ2fp_r); - if(fp_w != stdout) fclose(fp_w); - }else{ - BZFILE *BZ2fp_w = NULL; - FILE *fp_r = NULL; - - if(fn_r){ - if((fp_r = fopen(fn_r,"rb"))==NULL){ - printf("can't open [%s]\n",fn_r); - perror("reason:"); - exit(1); - } - }else{ - fp_r = stdin; - } - mode[0]='w'; - mode[1] = '0' + level; - mode[2] = '\0'; - - if((fn_w == NULL && (BZ2fp_w = BZ2_bzdopen(fileno(stdout),mode))==NULL) - || (fn_w !=NULL && (BZ2fp_w = BZ2_bzopen(fn_w,mode))==NULL)){ - printf("can't bz2openstream\n"); - exit(1); - } - while((len=fread(buff,1,0x1000,fp_r))>0){ - BZ2_bzwrite(BZ2fp_w,buff,len); - } - BZ2_bzclose(BZ2fp_w); - if(fp_r!=stdin)fclose(fp_r); - } - } -#ifdef _WIN32 - BZ2DLLFreeLibrary(); -#endif - return 0; -} +/* + minibz2 + libbz2.dll test program. + by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + This file is Public Domain. Welcome any email to me. + + usage: minibz2 [-d] [-{1,2,..9}] [[srcfilename] destfilename] +*/ + +#define BZ_IMPORT +#include +#include +#include "bzlib.h" +#ifdef _WIN32 +#include +#endif + + +#ifdef _WIN32 + +#define BZ2_LIBNAME "libbz2-1.0.2.DLL" + +#include +static int BZ2DLLLoaded = 0; +static HINSTANCE BZ2DLLhLib; +int BZ2DLLLoadLibrary(void) +{ + HINSTANCE hLib; + + if(BZ2DLLLoaded==1){return 0;} + hLib=LoadLibrary(BZ2_LIBNAME); + if(hLib == NULL){ + fprintf(stderr,"Can't load %s\n",BZ2_LIBNAME); + return -1; + } + BZ2_bzlibVersion=GetProcAddress(hLib,"BZ2_bzlibVersion"); + BZ2_bzopen=GetProcAddress(hLib,"BZ2_bzopen"); + BZ2_bzdopen=GetProcAddress(hLib,"BZ2_bzdopen"); + BZ2_bzread=GetProcAddress(hLib,"BZ2_bzread"); + BZ2_bzwrite=GetProcAddress(hLib,"BZ2_bzwrite"); + BZ2_bzflush=GetProcAddress(hLib,"BZ2_bzflush"); + BZ2_bzclose=GetProcAddress(hLib,"BZ2_bzclose"); + BZ2_bzerror=GetProcAddress(hLib,"BZ2_bzerror"); + + if (!BZ2_bzlibVersion || !BZ2_bzopen || !BZ2_bzdopen + || !BZ2_bzread || !BZ2_bzwrite || !BZ2_bzflush + || !BZ2_bzclose || !BZ2_bzerror) { + fprintf(stderr,"GetProcAddress failed.\n"); + return -1; + } + BZ2DLLLoaded=1; + BZ2DLLhLib=hLib; + return 0; + +} +int BZ2DLLFreeLibrary(void) +{ + if(BZ2DLLLoaded==0){return 0;} + FreeLibrary(BZ2DLLhLib); + BZ2DLLLoaded=0; +} +#endif /* WIN32 */ + +void usage(void) +{ + puts("usage: minibz2 [-d] [-{1,2,..9}] [[srcfilename] destfilename]"); +} + +int main(int argc,char *argv[]) +{ + int decompress = 0; + int level = 9; + char *fn_r = NULL; + char *fn_w = NULL; + +#ifdef _WIN32 + if(BZ2DLLLoadLibrary()<0){ + fprintf(stderr,"Loading of %s failed. Giving up.\n", BZ2_LIBNAME); + exit(1); + } + printf("Loading of %s succeeded. Library version is %s.\n", + BZ2_LIBNAME, BZ2_bzlibVersion() ); +#endif + while(++argv,--argc){ + if(**argv =='-' || **argv=='/'){ + char *p; + + for(p=*argv+1;*p;p++){ + if(*p=='d'){ + decompress = 1; + }else if('1'<=*p && *p<='9'){ + level = *p - '0'; + }else{ + usage(); + exit(1); + } + } + }else{ + break; + } + } + if(argc>=1){ + fn_r = *argv; + argc--;argv++; + }else{ + fn_r = NULL; + } + if(argc>=1){ + fn_w = *argv; + argc--;argv++; + }else{ + fn_w = NULL; + } + { + int len; + char buff[0x1000]; + char mode[10]; + + if(decompress){ + BZFILE *BZ2fp_r = NULL; + FILE *fp_w = NULL; + + if(fn_w){ + if((fp_w = fopen(fn_w,"wb"))==NULL){ + printf("can't open [%s]\n",fn_w); + perror("reason:"); + exit(1); + } + }else{ + fp_w = stdout; + } + if((fn_r == NULL && (BZ2fp_r = BZ2_bzdopen(fileno(stdin),"rb"))==NULL) + || (fn_r != NULL && (BZ2fp_r = BZ2_bzopen(fn_r,"rb"))==NULL)){ + printf("can't bz2openstream\n"); + exit(1); + } + while((len=BZ2_bzread(BZ2fp_r,buff,0x1000))>0){ + fwrite(buff,1,len,fp_w); + } + BZ2_bzclose(BZ2fp_r); + if(fp_w != stdout) fclose(fp_w); + }else{ + BZFILE *BZ2fp_w = NULL; + FILE *fp_r = NULL; + + if(fn_r){ + if((fp_r = fopen(fn_r,"rb"))==NULL){ + printf("can't open [%s]\n",fn_r); + perror("reason:"); + exit(1); + } + }else{ + fp_r = stdin; + } + mode[0]='w'; + mode[1] = '0' + level; + mode[2] = '\0'; + + if((fn_w == NULL && (BZ2fp_w = BZ2_bzdopen(fileno(stdout),mode))==NULL) + || (fn_w !=NULL && (BZ2fp_w = BZ2_bzopen(fn_w,mode))==NULL)){ + printf("can't bz2openstream\n"); + exit(1); + } + while((len=fread(buff,1,0x1000,fp_r))>0){ + BZ2_bzwrite(BZ2fp_w,buff,len); + } + BZ2_bzclose(BZ2fp_w); + if(fp_r!=stdin)fclose(fp_r); + } + } +#ifdef _WIN32 + BZ2DLLFreeLibrary(); +#endif + return 0; +} diff --git a/Utilities/cmbzip2/entities.xml b/Utilities/cmbzip2/entities.xml deleted file mode 100644 index e9e0553b7db..00000000000 --- a/Utilities/cmbzip2/entities.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Utilities/cmbzip2/format.pl b/Utilities/cmbzip2/format.pl deleted file mode 100755 index 2b391dad64f..00000000000 --- a/Utilities/cmbzip2/format.pl +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/perl -w -# -# ------------------------------------------------------------------ -# This file is part of bzip2/libbzip2, a program and library for -# lossless, block-sorting data compression. -# -# bzip2/libbzip2 version 1.0.5 of 10 December 2007 -# Copyright (C) 1996-2007 Julian Seward -# -# Please read the WARNING, DISCLAIMER and PATENTS sections in the -# README file. -# -# This program is released under the terms of the license contained -# in the file LICENSE. -# ------------------------------------------------------------------ -# -use strict; - -# get command line values: -if ( $#ARGV !=1 ) { - die "Usage: $0 xml_infile xml_outfile\n"; -} - -my $infile = shift; -# check infile exists -die "Can't find file \"$infile\"" - unless -f $infile; -# check we can read infile -if (! -r $infile) { - die "Can't read input $infile\n"; -} -# check we can open infile -open( INFILE,"<$infile" ) or - die "Can't input $infile $!"; - -#my $outfile = 'fmt-manual.xml'; -my $outfile = shift; -#print "Infile: $infile, Outfile: $outfile\n"; -# check we can write to outfile -open( OUTFILE,">$outfile" ) or - die "Can't output $outfile $! for writing"; - -my ($prev, $curr, $str); -$prev = ''; $curr = ''; -while ( ) { - - print OUTFILE $prev; - $prev = $curr; - $curr = $_; - $str = ''; - - if ( $prev =~ /$|$/ ) { - chomp $prev; - $curr = join( '', $prev, "|<\/screen>/ ) { - chomp $prev; - $curr = join( '', $prev, "]]>", $curr ); - $prev = ''; - next; - } -} -print OUTFILE $curr; -close INFILE; -close OUTFILE; -exit; diff --git a/Utilities/cmbzip2/huffman.c b/Utilities/cmbzip2/huffman.c index 87e79e38af0..43a1899e468 100644 --- a/Utilities/cmbzip2/huffman.c +++ b/Utilities/cmbzip2/huffman.c @@ -8,8 +8,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. diff --git a/Utilities/cmbzip2/libbz2.def b/Utilities/cmbzip2/libbz2.def deleted file mode 100644 index 69fef54bc70..00000000000 --- a/Utilities/cmbzip2/libbz2.def +++ /dev/null @@ -1,27 +0,0 @@ -LIBRARY LIBBZ2 -DESCRIPTION "libbzip2: library for data compression" -EXPORTS - BZ2_bzCompressInit - BZ2_bzCompress - BZ2_bzCompressEnd - BZ2_bzDecompressInit - BZ2_bzDecompress - BZ2_bzDecompressEnd - BZ2_bzReadOpen - BZ2_bzReadClose - BZ2_bzReadGetUnused - BZ2_bzRead - BZ2_bzWriteOpen - BZ2_bzWrite - BZ2_bzWriteClose - BZ2_bzWriteClose64 - BZ2_bzBuffToBuffCompress - BZ2_bzBuffToBuffDecompress - BZ2_bzlibVersion - BZ2_bzopen - BZ2_bzdopen - BZ2_bzread - BZ2_bzwrite - BZ2_bzflush - BZ2_bzclose - BZ2_bzerror diff --git a/Utilities/cmbzip2/libbz2.lib b/Utilities/cmbzip2/libbz2.lib deleted file mode 100644 index 9a97a75e24f..00000000000 Binary files a/Utilities/cmbzip2/libbz2.lib and /dev/null differ diff --git a/Utilities/cmbzip2/makefile.msc b/Utilities/cmbzip2/makefile.msc deleted file mode 100644 index d5f2e593037..00000000000 --- a/Utilities/cmbzip2/makefile.msc +++ /dev/null @@ -1,63 +0,0 @@ -# Makefile for Microsoft Visual C++ 6.0 -# usage: nmake -f makefile.msc -# K.M. Syring (syring@gsf.de) -# Fixed up by JRS for bzip2-0.9.5d release. - -CC=cl -CFLAGS= -DWIN32 -MD -Ox -D_FILE_OFFSET_BITS=64 -nologo - -OBJS= blocksort.obj \ - huffman.obj \ - crctable.obj \ - randtable.obj \ - compress.obj \ - decompress.obj \ - bzlib.obj - -all: lib bzip2 test - -bzip2: lib - $(CC) $(CFLAGS) -o bzip2 bzip2.c libbz2.lib setargv.obj - $(CC) $(CFLAGS) -o bzip2recover bzip2recover.c - -lib: $(OBJS) - lib /out:libbz2.lib $(OBJS) - -test: bzip2 - type words1 - .\\bzip2 -1 < sample1.ref > sample1.rb2 - .\\bzip2 -2 < sample2.ref > sample2.rb2 - .\\bzip2 -3 < sample3.ref > sample3.rb2 - .\\bzip2 -d < sample1.bz2 > sample1.tst - .\\bzip2 -d < sample2.bz2 > sample2.tst - .\\bzip2 -ds < sample3.bz2 > sample3.tst - @echo All six of the fc's should find no differences. - @echo If fc finds an error on sample3.bz2, this could be - @echo because WinZip's 'TAR file smart CR/LF conversion' - @echo is too clever for its own good. Disable this option. - @echo The correct size for sample3.ref is 120,244. If it - @echo is 150,251, WinZip has messed it up. - fc sample1.bz2 sample1.rb2 - fc sample2.bz2 sample2.rb2 - fc sample3.bz2 sample3.rb2 - fc sample1.tst sample1.ref - fc sample2.tst sample2.ref - fc sample3.tst sample3.ref - - - -clean: - del *.obj - del libbz2.lib - del bzip2.exe - del bzip2recover.exe - del sample1.rb2 - del sample2.rb2 - del sample3.rb2 - del sample1.tst - del sample2.tst - del sample3.tst - -.c.obj: - $(CC) $(CFLAGS) -c $*.c -o $*.obj - diff --git a/Utilities/cmbzip2/manual.html b/Utilities/cmbzip2/manual.html deleted file mode 100644 index bb4495385bd..00000000000 --- a/Utilities/cmbzip2/manual.html +++ /dev/null @@ -1,2540 +0,0 @@ - - - -bzip2 and libbzip2, version 1.0.5 - - - -
-
-
-

-bzip2 and libbzip2, version 1.0.5

-

A program and library for data compression

-
-

-Julian Seward -

-
http://www.bzip.org
-
-

Version 1.0.5 of 10 December 2007

-
-
-

This program, bzip2, the - associated library libbzip2, and - all documentation, are copyright © 1996-2007 Julian Seward. - All rights reserved.

-

Redistribution and use in source and binary forms, with - or without modification, are permitted provided that the - following conditions are met:

-
    -
  • Redistributions of source code must retain the - above copyright notice, this list of conditions and the - following disclaimer.

  • -
  • The origin of this software must not be - misrepresented; you must not claim that you wrote the original - software. If you use this software in a product, an - acknowledgment in the product documentation would be - appreciated but is not required.

  • -
  • Altered source versions must be plainly marked - as such, and must not be misrepresented as being the original - software.

  • -
  • The name of the author may not be used to - endorse or promote products derived from this software without - specific prior written permission.

  • -
-

THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE.

-

PATENTS: To the best of my knowledge, - bzip2 and - libbzip2 do not use any patented - algorithms. However, I do not have the resources to carry - out a patent search. Therefore I cannot give any guarantee of - the above statement. -

-
-
-
-
- -
-

-1. Introduction

-

bzip2 compresses files -using the Burrows-Wheeler block-sorting text compression -algorithm, and Huffman coding. Compression is generally -considerably better than that achieved by more conventional -LZ77/LZ78-based compressors, and approaches the performance of -the PPM family of statistical compressors.

-

bzip2 is built on top of -libbzip2, a flexible library for -handling compressed data in the -bzip2 format. This manual -describes both how to use the program and how to work with the -library interface. Most of the manual is devoted to this -library, not the program, which is good news if your interest is -only in the program.

-
    -
  • How to use bzip2 describes how to use - bzip2; this is the only part - you need to read if you just want to know how to operate the - program.

  • -
  • Programming with libbzip2 describes the - programming interfaces in detail, and

  • -
  • Miscellanea records some - miscellaneous notes which I thought ought to be recorded - somewhere.

  • -
-
-
-

-2. How to use bzip2

- -

This chapter contains a copy of the -bzip2 man page, and nothing -else.

-
-

-2.1. NAME

-
    -
  • bzip2, - bunzip2 - a block-sorting file - compressor, v1.0.4

  • -
  • bzcat - - decompresses files to stdout

  • -
  • bzip2recover - - recovers data from damaged bzip2 files

  • -
-
-
-

-2.2. SYNOPSIS

-
    -
  • bzip2 [ - -cdfkqstvzVL123456789 ] [ filenames ... ]

  • -
  • bunzip2 [ - -fkvsVL ] [ filenames ... ]

  • -
  • bzcat [ -s ] [ - filenames ... ]

  • -
  • bzip2recover - filename

  • -
-
-
-

-2.3. DESCRIPTION

-

bzip2 compresses files -using the Burrows-Wheeler block sorting text compression -algorithm, and Huffman coding. Compression is generally -considerably better than that achieved by more conventional -LZ77/LZ78-based compressors, and approaches the performance of -the PPM family of statistical compressors.

-

The command-line options are deliberately very similar to -those of GNU gzip, but they are -not identical.

-

bzip2 expects a list of -file names to accompany the command-line flags. Each file is -replaced by a compressed version of itself, with the name -original_name.bz2. Each -compressed file has the same modification date, permissions, and, -when possible, ownership as the corresponding original, so that -these properties can be correctly restored at decompression time. -File name handling is naive in the sense that there is no -mechanism for preserving original file names, permissions, -ownerships or dates in filesystems which lack these concepts, or -have serious file name length restrictions, such as -MS-DOS.

-

bzip2 and -bunzip2 will by default not -overwrite existing files. If you want this to happen, specify -the -f flag.

-

If no file names are specified, -bzip2 compresses from standard -input to standard output. In this case, -bzip2 will decline to write -compressed output to a terminal, as this would be entirely -incomprehensible and therefore pointless.

-

bunzip2 (or -bzip2 -d) decompresses all -specified files. Files which were not created by -bzip2 will be detected and -ignored, and a warning issued. -bzip2 attempts to guess the -filename for the decompressed file from that of the compressed -file as follows:

-
    -
  • filename.bz2 - becomes - filename

  • -
  • filename.bz - becomes - filename

  • -
  • filename.tbz2 - becomes - filename.tar

  • -
  • filename.tbz - becomes - filename.tar

  • -
  • anyothername - becomes - anyothername.out

  • -
-

If the file does not end in one of the recognised endings, -.bz2, -.bz, -.tbz2 or -.tbz, -bzip2 complains that it cannot -guess the name of the original file, and uses the original name -with .out appended.

-

As with compression, supplying no filenames causes -decompression from standard input to standard output.

-

bunzip2 will correctly -decompress a file which is the concatenation of two or more -compressed files. The result is the concatenation of the -corresponding uncompressed files. Integrity testing -(-t) of concatenated compressed -files is also supported.

-

You can also compress or decompress files to the standard -output by giving the -c flag. -Multiple files may be compressed and decompressed like this. The -resulting outputs are fed sequentially to stdout. Compression of -multiple files in this manner generates a stream containing -multiple compressed file representations. Such a stream can be -decompressed correctly only by -bzip2 version 0.9.0 or later. -Earlier versions of bzip2 will -stop after decompressing the first file in the stream.

-

bzcat (or -bzip2 -dc) decompresses all -specified files to the standard output.

-

bzip2 will read arguments -from the environment variables -BZIP2 and -BZIP, in that order, and will -process them before any arguments read from the command line. -This gives a convenient way to supply default arguments.

-

Compression is always performed, even if the compressed -file is slightly larger than the original. Files of less than -about one hundred bytes tend to get larger, since the compression -mechanism has a constant overhead in the region of 50 bytes. -Random data (including the output of most file compressors) is -coded at about 8.05 bits per byte, giving an expansion of around -0.5%.

-

As a self-check for your protection, -bzip2 uses 32-bit CRCs to make -sure that the decompressed version of a file is identical to the -original. This guards against corruption of the compressed data, -and against undetected bugs in -bzip2 (hopefully very unlikely). -The chances of data corruption going undetected is microscopic, -about one chance in four billion for each file processed. Be -aware, though, that the check occurs upon decompression, so it -can only tell you that something is wrong. It can't help you -recover the original uncompressed data. You can use -bzip2recover to try to recover -data from damaged files.

-

Return values: 0 for a normal exit, 1 for environmental -problems (file not found, invalid flags, I/O errors, etc.), 2 -to indicate a corrupt compressed file, 3 for an internal -consistency error (eg, bug) which caused -bzip2 to panic.

-
-
-

-2.4. OPTIONS

-
-
-c --stdout
-

Compress or decompress to standard - output.

-
-d --decompress
-

Force decompression. - bzip2, - bunzip2 and - bzcat are really the same - program, and the decision about what actions to take is done on - the basis of which name is used. This flag overrides that - mechanism, and forces bzip2 to decompress.

-
-z --compress
-

The complement to - -d: forces compression, - regardless of the invokation name.

-
-t --test
-

Check integrity of the specified file(s), but - don't decompress them. This really performs a trial - decompression and throws away the result.

-
-f --force
-
-

Force overwrite of output files. Normally, - bzip2 will not overwrite - existing output files. Also forces - bzip2 to break hard links to - files, which it otherwise wouldn't do.

-

bzip2 normally declines - to decompress files which don't have the correct magic header - bytes. If forced (-f), - however, it will pass such files through unmodified. This is - how GNU gzip behaves.

-
-
-k --keep
-

Keep (don't delete) input files during - compression or decompression.

-
-s --small
-
-

Reduce memory usage, for compression, - decompression and testing. Files are decompressed and tested - using a modified algorithm which only requires 2.5 bytes per - block byte. This means any file can be decompressed in 2300k - of memory, albeit at about half the normal speed.

-

During compression, -s - selects a block size of 200k, which limits memory use to around - the same figure, at the expense of your compression ratio. In - short, if your machine is low on memory (8 megabytes or less), - use -s for everything. See - MEMORY MANAGEMENT below.

-
-
-q --quiet
-

Suppress non-essential warning messages. - Messages pertaining to I/O errors and other critical events - will not be suppressed.

-
-v --verbose
-

Verbose mode -- show the compression ratio for - each file processed. Further - -v's increase the verbosity - level, spewing out lots of information which is primarily of - interest for diagnostic purposes.

-
-L --license -V --version
-

Display the software version, license terms and - conditions.

-
-1 (or - --fast) to - -9 (or - -best)
-

Set the block size to 100 k, 200 k ... 900 k - when compressing. Has no effect when decompressing. See MEMORY MANAGEMENT below. The - --fast and - --best aliases are primarily - for GNU gzip compatibility. - In particular, --fast doesn't - make things significantly faster. And - --best merely selects the - default behaviour.

-
--
-

Treats all subsequent arguments as file names, - even if they start with a dash. This is so you can handle - files with names beginning with a dash, for example: - bzip2 -- - -myfilename.

-
---repetitive-fast, --repetitive-best -
-

These flags are redundant in versions 0.9.5 and - above. They provided some coarse control over the behaviour of - the sorting algorithm in earlier versions, which was sometimes - useful. 0.9.5 and above have an improved algorithm which - renders these flags irrelevant.

-
-
-
-

-2.5. MEMORY MANAGEMENT

-

bzip2 compresses large -files in blocks. The block size affects both the compression -ratio achieved, and the amount of memory needed for compression -and decompression. The flags -1 -through -9 specify the block -size to be 100,000 bytes through 900,000 bytes (the default) -respectively. At decompression time, the block size used for -compression is read from the header of the compressed file, and -bunzip2 then allocates itself -just enough memory to decompress the file. Since block sizes are -stored in compressed files, it follows that the flags --1 to --9 are irrelevant to and so -ignored during decompression.

-

Compression and decompression requirements, in bytes, can be -estimated as:

-
Compression:   400k + ( 8 x block size )
-
-Decompression: 100k + ( 4 x block size ), or
-               100k + ( 2.5 x block size )
-

Larger block sizes give rapidly diminishing marginal -returns. Most of the compression comes from the first two or -three hundred k of block size, a fact worth bearing in mind when -using bzip2 on small machines. -It is also important to appreciate that the decompression memory -requirement is set at compression time by the choice of block -size.

-

For files compressed with the default 900k block size, -bunzip2 will require about 3700 -kbytes to decompress. To support decompression of any file on a -4 megabyte machine, bunzip2 has -an option to decompress using approximately half this amount of -memory, about 2300 kbytes. Decompression speed is also halved, -so you should use this option only where necessary. The relevant -flag is -s.

-

In general, try and use the largest block size memory -constraints allow, since that maximises the compression achieved. -Compression and decompression speed are virtually unaffected by -block size.

-

Another significant point applies to files which fit in a -single block -- that means most files you'd encounter using a -large block size. The amount of real memory touched is -proportional to the size of the file, since the file is smaller -than a block. For example, compressing a file 20,000 bytes long -with the flag -9 will cause the -compressor to allocate around 7600k of memory, but only touch -400k + 20000 * 8 = 560 kbytes of it. Similarly, the decompressor -will allocate 3700k but only touch 100k + 20000 * 4 = 180 -kbytes.

-

Here is a table which summarises the maximum memory usage -for different block sizes. Also recorded is the total compressed -size for 14 files of the Calgary Text Compression Corpus -totalling 3,141,622 bytes. This column gives some feel for how -compression varies with block size. These figures tend to -understate the advantage of larger block sizes for larger files, -since the Corpus is dominated by smaller files.

-
        Compress   Decompress   Decompress   Corpus
-Flag     usage      usage       -s usage     Size
-
- -1      1200k       500k         350k      914704
- -2      2000k       900k         600k      877703
- -3      2800k      1300k         850k      860338
- -4      3600k      1700k        1100k      846899
- -5      4400k      2100k        1350k      845160
- -6      5200k      2500k        1600k      838626
- -7      6100k      2900k        1850k      834096
- -8      6800k      3300k        2100k      828642
- -9      7600k      3700k        2350k      828642
-
-
-

-2.6. RECOVERING DATA FROM DAMAGED FILES

-

bzip2 compresses files in -blocks, usually 900kbytes long. Each block is handled -independently. If a media or transmission error causes a -multi-block .bz2 file to become -damaged, it may be possible to recover data from the undamaged -blocks in the file.

-

The compressed representation of each block is delimited by -a 48-bit pattern, which makes it possible to find the block -boundaries with reasonable certainty. Each block also carries -its own 32-bit CRC, so damaged blocks can be distinguished from -undamaged ones.

-

bzip2recover is a simple -program whose purpose is to search for blocks in -.bz2 files, and write each block -out into its own .bz2 file. You -can then use bzip2 -t to test -the integrity of the resulting files, and decompress those which -are undamaged.

-

bzip2recover takes a -single argument, the name of the damaged file, and writes a -number of files rec0001file.bz2, -rec0002file.bz2, etc, containing -the extracted blocks. The output filenames are designed so that -the use of wildcards in subsequent processing -- for example, -bzip2 -dc rec*file.bz2 > -recovered_data -- lists the files in the correct -order.

-

bzip2recover should be of -most use dealing with large .bz2 -files, as these will contain many blocks. It is clearly futile -to use it on damaged single-block files, since a damaged block -cannot be recovered. If you wish to minimise any potential data -loss through media or transmission errors, you might consider -compressing with a smaller block size.

-
-
-

-2.7. PERFORMANCE NOTES

-

The sorting phase of compression gathers together similar -strings in the file. Because of this, files containing very long -runs of repeated symbols, like "aabaabaabaab ..." (repeated -several hundred times) may compress more slowly than normal. -Versions 0.9.5 and above fare much better than previous versions -in this respect. The ratio between worst-case and average-case -compression time is in the region of 10:1. For previous -versions, this figure was more like 100:1. You can use the --vvvv option to monitor progress -in great detail, if you want.

-

Decompression speed is unaffected by these -phenomena.

-

bzip2 usually allocates -several megabytes of memory to operate in, and then charges all -over it in a fairly random fashion. This means that performance, -both for compressing and decompressing, is largely determined by -the speed at which your machine can service cache misses. -Because of this, small changes to the code to reduce the miss -rate have been observed to give disproportionately large -performance improvements. I imagine -bzip2 will perform best on -machines with very large caches.

-
-
-

-2.8. CAVEATS

-

I/O error messages are not as helpful as they could be. -bzip2 tries hard to detect I/O -errors and exit cleanly, but the details of what the problem is -sometimes seem rather misleading.

-

This manual page pertains to version 1.0.5 of -bzip2. Compressed data created by -this version is entirely forwards and backwards compatible with the -previous public releases, versions 0.1pl2, 0.9.0 and 0.9.5, 1.0.0, -1.0.1, 1.0.2 and 1.0.3, but with the following exception: 0.9.0 and -above can correctly decompress multiple concatenated compressed files. -0.1pl2 cannot do this; it will stop after decompressing just the first -file in the stream.

-

bzip2recover versions -prior to 1.0.2 used 32-bit integers to represent bit positions in -compressed files, so it could not handle compressed files more -than 512 megabytes long. Versions 1.0.2 and above use 64-bit ints -on some platforms which support them (GNU supported targets, and -Windows). To establish whether or not -bzip2recover was built with such -a limitation, run it without arguments. In any event you can -build yourself an unlimited version if you can recompile it with -MaybeUInt64 set to be an -unsigned 64-bit integer.

-
-
-

-2.9. AUTHOR

-

Julian Seward, -jseward@bzip.org

-

The ideas embodied in -bzip2 are due to (at least) the -following people: Michael Burrows and David Wheeler (for the -block sorting transformation), David Wheeler (again, for the -Huffman coder), Peter Fenwick (for the structured coding model in -the original bzip, and many -refinements), and Alistair Moffat, Radford Neal and Ian Witten -(for the arithmetic coder in the original -bzip). I am much indebted for -their help, support and advice. See the manual in the source -distribution for pointers to sources of documentation. Christian -von Roques encouraged me to look for faster sorting algorithms, -so as to speed up compression. Bela Lubkin encouraged me to -improve the worst-case compression performance. -Donna Robinson XMLised the documentation. -Many people sent -patches, helped with portability problems, lent machines, gave -advice and were generally helpful.

-
-
-
-

-3.  -Programming with libbzip2 -

- -

This chapter describes the programming interface to -libbzip2.

-

For general background information, particularly about -memory use and performance aspects, you'd be well advised to read -How to use bzip2 as well.

-
-

-3.1. Top-level structure

-

libbzip2 is a flexible -library for compressing and decompressing data in the -bzip2 data format. Although -packaged as a single entity, it helps to regard the library as -three separate parts: the low level interface, and the high level -interface, and some utility functions.

-

The structure of -libbzip2's interfaces is similar -to that of Jean-loup Gailly's and Mark Adler's excellent -zlib library.

-

All externally visible symbols have names beginning -BZ2_. This is new in version -1.0. The intention is to minimise pollution of the namespaces of -library clients.

-

To use any part of the library, you need to -#include <bzlib.h> -into your sources.

-
-

-3.1.1. Low-level summary

-

This interface provides services for compressing and -decompressing data in memory. There's no provision for dealing -with files, streams or any other I/O mechanisms, just straight -memory-to-memory work. In fact, this part of the library can be -compiled without inclusion of -stdio.h, which may be helpful -for embedded applications.

-

The low-level part of the library has no global variables -and is therefore thread-safe.

-

Six routines make up the low level interface: -BZ2_bzCompressInit, -BZ2_bzCompress, and -BZ2_bzCompressEnd for -compression, and a corresponding trio -BZ2_bzDecompressInit, -BZ2_bzDecompress and -BZ2_bzDecompressEnd for -decompression. The *Init -functions allocate memory for compression/decompression and do -other initialisations, whilst the -*End functions close down -operations and release memory.

-

The real work is done by -BZ2_bzCompress and -BZ2_bzDecompress. These -compress and decompress data from a user-supplied input buffer to -a user-supplied output buffer. These buffers can be any size; -arbitrary quantities of data are handled by making repeated calls -to these functions. This is a flexible mechanism allowing a -consumer-pull style of activity, or producer-push, or a mixture -of both.

-
-
-

-3.1.2. High-level summary

-

This interface provides some handy wrappers around the -low-level interface to facilitate reading and writing -bzip2 format files -(.bz2 files). The routines -provide hooks to facilitate reading files in which the -bzip2 data stream is embedded -within some larger-scale file structure, or where there are -multiple bzip2 data streams -concatenated end-to-end.

-

For reading files, -BZ2_bzReadOpen, -BZ2_bzRead, -BZ2_bzReadClose and -BZ2_bzReadGetUnused are -supplied. For writing files, -BZ2_bzWriteOpen, -BZ2_bzWrite and -BZ2_bzWriteFinish are -available.

-

As with the low-level library, no global variables are used -so the library is per se thread-safe. However, if I/O errors -occur whilst reading or writing the underlying compressed files, -you may have to consult errno to -determine the cause of the error. In that case, you'd need a C -library which correctly supports -errno in a multithreaded -environment.

-

To make the library a little simpler and more portable, -BZ2_bzReadOpen and -BZ2_bzWriteOpen require you to -pass them file handles (FILE*s) -which have previously been opened for reading or writing -respectively. That avoids portability problems associated with -file operations and file attributes, whilst not being much of an -imposition on the programmer.

-
-
-

-3.1.3. Utility functions summary

-

For very simple needs, -BZ2_bzBuffToBuffCompress and -BZ2_bzBuffToBuffDecompress are -provided. These compress data in memory from one buffer to -another buffer in a single function call. You should assess -whether these functions fulfill your memory-to-memory -compression/decompression requirements before investing effort in -understanding the more general but more complex low-level -interface.

-

Yoshioka Tsuneo -(tsuneo@rr.iij4u.or.jp) has -contributed some functions to give better -zlib compatibility. These -functions are BZ2_bzopen, -BZ2_bzread, -BZ2_bzwrite, -BZ2_bzflush, -BZ2_bzclose, -BZ2_bzerror and -BZ2_bzlibVersion. You may find -these functions more convenient for simple file reading and -writing, than those in the high-level interface. These functions -are not (yet) officially part of the library, and are minimally -documented here. If they break, you get to keep all the pieces. -I hope to document them properly when time permits.

-

Yoshioka also contributed modifications to allow the -library to be built as a Windows DLL.

-
-
-
-

-3.2. Error handling

-

The library is designed to recover cleanly in all -situations, including the worst-case situation of decompressing -random data. I'm not 100% sure that it can always do this, so -you might want to add a signal handler to catch segmentation -violations during decompression if you are feeling especially -paranoid. I would be interested in hearing more about the -robustness of the library to corrupted compressed data.

-

Version 1.0.3 more robust in this respect than any -previous version. Investigations with Valgrind (a tool for detecting -problems with memory management) indicate -that, at least for the few files I tested, all single-bit errors -in the decompressed data are caught properly, with no -segmentation faults, no uses of uninitialised data, no out of -range reads or writes, and no infinite looping in the decompressor. -So it's certainly pretty robust, although -I wouldn't claim it to be totally bombproof.

-

The file bzlib.h contains -all definitions needed to use the library. In particular, you -should definitely not include -bzlib_private.h.

-

In bzlib.h, the various -return values are defined. The following list is not intended as -an exhaustive description of the circumstances in which a given -value may be returned -- those descriptions are given later. -Rather, it is intended to convey the rough meaning of each return -value. The first five actions are normal and not intended to -denote an error situation.

-
-
BZ_OK
-

The requested action was completed - successfully.

-
BZ_RUN_OK, BZ_FLUSH_OK, - BZ_FINISH_OK
-

In - BZ2_bzCompress, the requested - flush/finish/nothing-special action was completed - successfully.

-
BZ_STREAM_END
-

Compression of data was completed, or the - logical stream end was detected during - decompression.

-
-

The following return values indicate an error of some -kind.

-
-
BZ_CONFIG_ERROR
-

Indicates that the library has been improperly - compiled on your platform -- a major configuration error. - Specifically, it means that - sizeof(char), - sizeof(short) and - sizeof(int) are not 1, 2 and - 4 respectively, as they should be. Note that the library - should still work properly on 64-bit platforms which follow - the LP64 programming model -- that is, where - sizeof(long) and - sizeof(void*) are 8. Under - LP64, sizeof(int) is still 4, - so libbzip2, which doesn't - use the long type, is - OK.

-
BZ_SEQUENCE_ERROR
-

When using the library, it is important to call - the functions in the correct sequence and with data structures - (buffers etc) in the correct states. - libbzip2 checks as much as it - can to ensure this is happening, and returns - BZ_SEQUENCE_ERROR if not. - Code which complies precisely with the function semantics, as - detailed below, should never receive this value; such an event - denotes buggy code which you should - investigate.

-
BZ_PARAM_ERROR
-

Returned when a parameter to a function call is - out of range or otherwise manifestly incorrect. As with - BZ_SEQUENCE_ERROR, this - denotes a bug in the client code. The distinction between - BZ_PARAM_ERROR and - BZ_SEQUENCE_ERROR is a bit - hazy, but still worth making.

-
BZ_MEM_ERROR
-

Returned when a request to allocate memory - failed. Note that the quantity of memory needed to decompress - a stream cannot be determined until the stream's header has - been read. So - BZ2_bzDecompress and - BZ2_bzRead may return - BZ_MEM_ERROR even though some - of the compressed data has been read. The same is not true - for compression; once - BZ2_bzCompressInit or - BZ2_bzWriteOpen have - successfully completed, - BZ_MEM_ERROR cannot - occur.

-
BZ_DATA_ERROR
-

Returned when a data integrity error is - detected during decompression. Most importantly, this means - when stored and computed CRCs for the data do not match. This - value is also returned upon detection of any other anomaly in - the compressed data.

-
BZ_DATA_ERROR_MAGIC
-

As a special case of - BZ_DATA_ERROR, it is - sometimes useful to know when the compressed stream does not - start with the correct magic bytes ('B' 'Z' - 'h').

-
BZ_IO_ERROR
-

Returned by - BZ2_bzRead and - BZ2_bzWrite when there is an - error reading or writing in the compressed file, and by - BZ2_bzReadOpen and - BZ2_bzWriteOpen for attempts - to use a file for which the error indicator (viz, - ferror(f)) is set. On - receipt of BZ_IO_ERROR, the - caller should consult errno - and/or perror to acquire - operating-system specific information about the - problem.

-
BZ_UNEXPECTED_EOF
-

Returned by - BZ2_bzRead when the - compressed file finishes before the logical end of stream is - detected.

-
BZ_OUTBUFF_FULL
-

Returned by - BZ2_bzBuffToBuffCompress and - BZ2_bzBuffToBuffDecompress to - indicate that the output data will not fit into the output - buffer provided.

-
-
-
-

-3.3. Low-level interface

-
-

-3.3.1. BZ2_bzCompressInit

-
typedef struct {
-  char *next_in;
-  unsigned int avail_in;
-  unsigned int total_in_lo32;
-  unsigned int total_in_hi32;
-
-  char *next_out;
-  unsigned int avail_out;
-  unsigned int total_out_lo32;
-  unsigned int total_out_hi32;
-
-  void *state;
-
-  void *(*bzalloc)(void *,int,int);
-  void (*bzfree)(void *,void *);
-  void *opaque;
-} bz_stream;
-
-int BZ2_bzCompressInit ( bz_stream *strm, 
-                         int blockSize100k, 
-                         int verbosity,
-                         int workFactor );
-

Prepares for compression. The -bz_stream structure holds all -data pertaining to the compression activity. A -bz_stream structure should be -allocated and initialised prior to the call. The fields of -bz_stream comprise the entirety -of the user-visible data. state -is a pointer to the private data structures required for -compression.

-

Custom memory allocators are supported, via fields -bzalloc, -bzfree, and -opaque. The value -opaque is passed to as the first -argument to all calls to bzalloc -and bzfree, but is otherwise -ignored by the library. The call bzalloc ( -opaque, n, m ) is expected to return a pointer -p to n * -m bytes of memory, and bzfree ( -opaque, p ) should free that memory.

-

If you don't want to use a custom memory allocator, set -bzalloc, -bzfree and -opaque to -NULL, and the library will then -use the standard malloc / -free routines.

-

Before calling -BZ2_bzCompressInit, fields -bzalloc, -bzfree and -opaque should be filled -appropriately, as just described. Upon return, the internal -state will have been allocated and initialised, and -total_in_lo32, -total_in_hi32, -total_out_lo32 and -total_out_hi32 will have been -set to zero. These four fields are used by the library to inform -the caller of the total amount of data passed into and out of the -library, respectively. You should not try to change them. As of -version 1.0, 64-bit counts are maintained, even on 32-bit -platforms, using the _hi32 -fields to store the upper 32 bits of the count. So, for example, -the total amount of data in is (total_in_hi32 -<< 32) + total_in_lo32.

-

Parameter blockSize100k -specifies the block size to be used for compression. It should -be a value between 1 and 9 inclusive, and the actual block size -used is 100000 x this figure. 9 gives the best compression but -takes most memory.

-

Parameter verbosity should -be set to a number between 0 and 4 inclusive. 0 is silent, and -greater numbers give increasingly verbose monitoring/debugging -output. If the library has been compiled with --DBZ_NO_STDIO, no such output -will appear for any verbosity setting.

-

Parameter workFactor -controls how the compression phase behaves when presented with -worst case, highly repetitive, input data. If compression runs -into difficulties caused by repetitive data, the library switches -from the standard sorting algorithm to a fallback algorithm. The -fallback is slower than the standard algorithm by perhaps a -factor of three, but always behaves reasonably, no matter how bad -the input.

-

Lower values of workFactor -reduce the amount of effort the standard algorithm will expend -before resorting to the fallback. You should set this parameter -carefully; too low, and many inputs will be handled by the -fallback algorithm and so compress rather slowly, too high, and -your average-to-worst case compression times can become very -large. The default value of 30 gives reasonable behaviour over a -wide range of circumstances.

-

Allowable values range from 0 to 250 inclusive. 0 is a -special case, equivalent to using the default value of 30.

-

Note that the compressed output generated is the same -regardless of whether or not the fallback algorithm is -used.

-

Be aware also that this parameter may disappear entirely in -future versions of the library. In principle it should be -possible to devise a good way to automatically choose which -algorithm to use. Such a mechanism would render the parameter -obsolete.

-

Possible return values:

-
BZ_CONFIG_ERROR
-  if the library has been mis-compiled
-BZ_PARAM_ERROR
-  if strm is NULL 
-  or blockSize < 1 or blockSize > 9
-  or verbosity < 0 or verbosity > 4
-  or workFactor < 0 or workFactor > 250
-BZ_MEM_ERROR 
-  if not enough memory is available
-BZ_OK 
-  otherwise
-

Allowable next actions:

-
BZ2_bzCompress
-  if BZ_OK is returned
-  no specific action needed in case of error
-
-
-

-3.3.2. BZ2_bzCompress

-
int BZ2_bzCompress ( bz_stream *strm, int action );
-

Provides more input and/or output buffer space for the -library. The caller maintains input and output buffers, and -calls BZ2_bzCompress to transfer -data between them.

-

Before each call to -BZ2_bzCompress, -next_in should point at the data -to be compressed, and avail_in -should indicate how many bytes the library may read. -BZ2_bzCompress updates -next_in, -avail_in and -total_in to reflect the number -of bytes it has read.

-

Similarly, next_out should -point to a buffer in which the compressed data is to be placed, -with avail_out indicating how -much output space is available. -BZ2_bzCompress updates -next_out, -avail_out and -total_out to reflect the number -of bytes output.

-

You may provide and remove as little or as much data as you -like on each call of -BZ2_bzCompress. In the limit, -it is acceptable to supply and remove data one byte at a time, -although this would be terribly inefficient. You should always -ensure that at least one byte of output space is available at -each call.

-

A second purpose of -BZ2_bzCompress is to request a -change of mode of the compressed stream.

-

Conceptually, a compressed stream can be in one of four -states: IDLE, RUNNING, FLUSHING and FINISHING. Before -initialisation -(BZ2_bzCompressInit) and after -termination (BZ2_bzCompressEnd), -a stream is regarded as IDLE.

-

Upon initialisation -(BZ2_bzCompressInit), the stream -is placed in the RUNNING state. Subsequent calls to -BZ2_bzCompress should pass -BZ_RUN as the requested action; -other actions are illegal and will result in -BZ_SEQUENCE_ERROR.

-

At some point, the calling program will have provided all -the input data it wants to. It will then want to finish up -- in -effect, asking the library to process any data it might have -buffered internally. In this state, -BZ2_bzCompress will no longer -attempt to read data from -next_in, but it will want to -write data to next_out. Because -the output buffer supplied by the user can be arbitrarily small, -the finishing-up operation cannot necessarily be done with a -single call of -BZ2_bzCompress.

-

Instead, the calling program passes -BZ_FINISH as an action to -BZ2_bzCompress. This changes -the stream's state to FINISHING. Any remaining input (ie, -next_in[0 .. avail_in-1]) is -compressed and transferred to the output buffer. To do this, -BZ2_bzCompress must be called -repeatedly until all the output has been consumed. At that -point, BZ2_bzCompress returns -BZ_STREAM_END, and the stream's -state is set back to IDLE. -BZ2_bzCompressEnd should then be -called.

-

Just to make sure the calling program does not cheat, the -library makes a note of avail_in -at the time of the first call to -BZ2_bzCompress which has -BZ_FINISH as an action (ie, at -the time the program has announced its intention to not supply -any more input). By comparing this value with that of -avail_in over subsequent calls -to BZ2_bzCompress, the library -can detect any attempts to slip in more data to compress. Any -calls for which this is detected will return -BZ_SEQUENCE_ERROR. This -indicates a programming mistake which should be corrected.

-

Instead of asking to finish, the calling program may ask -BZ2_bzCompress to take all the -remaining input, compress it and terminate the current -(Burrows-Wheeler) compression block. This could be useful for -error control purposes. The mechanism is analogous to that for -finishing: call BZ2_bzCompress -with an action of BZ_FLUSH, -remove output data, and persist with the -BZ_FLUSH action until the value -BZ_RUN is returned. As with -finishing, BZ2_bzCompress -detects any attempt to provide more input data once the flush has -begun.

-

Once the flush is complete, the stream returns to the -normal RUNNING state.

-

This all sounds pretty complex, but isn't really. Here's a -table which shows which actions are allowable in each state, what -action will be taken, what the next state is, and what the -non-error return values are. Note that you can't explicitly ask -what state the stream is in, but nor do you need to -- it can be -inferred from the values returned by -BZ2_bzCompress.

-
IDLE/any
-  Illegal.  IDLE state only exists after BZ2_bzCompressEnd or
-  before BZ2_bzCompressInit.
-  Return value = BZ_SEQUENCE_ERROR
-
-RUNNING/BZ_RUN
-  Compress from next_in to next_out as much as possible.
-  Next state = RUNNING
-  Return value = BZ_RUN_OK
-
-RUNNING/BZ_FLUSH
-  Remember current value of next_in. Compress from next_in
-  to next_out as much as possible, but do not accept any more input.
-  Next state = FLUSHING
-  Return value = BZ_FLUSH_OK
-
-RUNNING/BZ_FINISH
-  Remember current value of next_in. Compress from next_in
-  to next_out as much as possible, but do not accept any more input.
-  Next state = FINISHING
-  Return value = BZ_FINISH_OK
-
-FLUSHING/BZ_FLUSH
-  Compress from next_in to next_out as much as possible, 
-  but do not accept any more input.
-  If all the existing input has been used up and all compressed
-  output has been removed
-    Next state = RUNNING; Return value = BZ_RUN_OK
-  else
-    Next state = FLUSHING; Return value = BZ_FLUSH_OK
-
-FLUSHING/other     
-  Illegal.
-  Return value = BZ_SEQUENCE_ERROR
-
-FINISHING/BZ_FINISH
-  Compress from next_in to next_out as much as possible,
-  but to not accept any more input.  
-  If all the existing input has been used up and all compressed
-  output has been removed
-    Next state = IDLE; Return value = BZ_STREAM_END
-  else
-    Next state = FINISHING; Return value = BZ_FINISH_OK
-
-FINISHING/other
-  Illegal.
-  Return value = BZ_SEQUENCE_ERROR
-

That still looks complicated? Well, fair enough. The -usual sequence of calls for compressing a load of data is:

-
    -
  1. Get started with - BZ2_bzCompressInit.

  2. -
  3. Shovel data in and shlurp out its compressed form - using zero or more calls of - BZ2_bzCompress with action = - BZ_RUN.

  4. -
  5. Finish up. Repeatedly call - BZ2_bzCompress with action = - BZ_FINISH, copying out the - compressed output, until - BZ_STREAM_END is - returned.

  6. -
  7. Close up and go home. Call - BZ2_bzCompressEnd.

  8. -
-

If the data you want to compress fits into your input -buffer all at once, you can skip the calls of -BZ2_bzCompress ( ..., BZ_RUN ) -and just do the BZ2_bzCompress ( ..., BZ_FINISH -) calls.

-

All required memory is allocated by -BZ2_bzCompressInit. The -compression library can accept any data at all (obviously). So -you shouldn't get any error return values from the -BZ2_bzCompress calls. If you -do, they will be -BZ_SEQUENCE_ERROR, and indicate -a bug in your programming.

-

Trivial other possible return values:

-
BZ_PARAM_ERROR
-  if strm is NULL, or strm->s is NULL
-
-
-

-3.3.3. BZ2_bzCompressEnd

-
int BZ2_bzCompressEnd ( bz_stream *strm );
-

Releases all memory associated with a compression -stream.

-

Possible return values:

-
BZ_PARAM_ERROR  if strm is NULL or strm->s is NULL
-BZ_OK           otherwise
-
-
-

-3.3.4. BZ2_bzDecompressInit

-
int BZ2_bzDecompressInit ( bz_stream *strm, int verbosity, int small );
-

Prepares for decompression. As with -BZ2_bzCompressInit, a -bz_stream record should be -allocated and initialised before the call. Fields -bzalloc, -bzfree and -opaque should be set if a custom -memory allocator is required, or made -NULL for the normal -malloc / -free routines. Upon return, the -internal state will have been initialised, and -total_in and -total_out will be zero.

-

For the meaning of parameter -verbosity, see -BZ2_bzCompressInit.

-

If small is nonzero, the -library will use an alternative decompression algorithm which -uses less memory but at the cost of decompressing more slowly -(roughly speaking, half the speed, but the maximum memory -requirement drops to around 2300k). See How to use bzip2 -for more information on memory management.

-

Note that the amount of memory needed to decompress a -stream cannot be determined until the stream's header has been -read, so even if -BZ2_bzDecompressInit succeeds, a -subsequent BZ2_bzDecompress -could fail with -BZ_MEM_ERROR.

-

Possible return values:

-
BZ_CONFIG_ERROR
-  if the library has been mis-compiled
-BZ_PARAM_ERROR
-  if ( small != 0 && small != 1 )
-  or (verbosity <; 0 || verbosity > 4)
-BZ_MEM_ERROR
-  if insufficient memory is available
-

Allowable next actions:

-
BZ2_bzDecompress
-  if BZ_OK was returned
-  no specific action required in case of error
-
-
-

-3.3.5. BZ2_bzDecompress

-
int BZ2_bzDecompress ( bz_stream *strm );
-

Provides more input and/out output buffer space for the -library. The caller maintains input and output buffers, and uses -BZ2_bzDecompress to transfer -data between them.

-

Before each call to -BZ2_bzDecompress, -next_in should point at the -compressed data, and avail_in -should indicate how many bytes the library may read. -BZ2_bzDecompress updates -next_in, -avail_in and -total_in to reflect the number -of bytes it has read.

-

Similarly, next_out should -point to a buffer in which the uncompressed output is to be -placed, with avail_out -indicating how much output space is available. -BZ2_bzCompress updates -next_out, -avail_out and -total_out to reflect the number -of bytes output.

-

You may provide and remove as little or as much data as you -like on each call of -BZ2_bzDecompress. In the limit, -it is acceptable to supply and remove data one byte at a time, -although this would be terribly inefficient. You should always -ensure that at least one byte of output space is available at -each call.

-

Use of BZ2_bzDecompress is -simpler than -BZ2_bzCompress.

-

You should provide input and remove output as described -above, and repeatedly call -BZ2_bzDecompress until -BZ_STREAM_END is returned. -Appearance of BZ_STREAM_END -denotes that BZ2_bzDecompress -has detected the logical end of the compressed stream. -BZ2_bzDecompress will not -produce BZ_STREAM_END until all -output data has been placed into the output buffer, so once -BZ_STREAM_END appears, you are -guaranteed to have available all the decompressed output, and -BZ2_bzDecompressEnd can safely -be called.

-

If case of an error return value, you should call -BZ2_bzDecompressEnd to clean up -and release memory.

-

Possible return values:

-
BZ_PARAM_ERROR
-  if strm is NULL or strm->s is NULL
-  or strm->avail_out < 1
-BZ_DATA_ERROR
-  if a data integrity error is detected in the compressed stream
-BZ_DATA_ERROR_MAGIC
-  if the compressed stream doesn't begin with the right magic bytes
-BZ_MEM_ERROR
-  if there wasn't enough memory available
-BZ_STREAM_END
-  if the logical end of the data stream was detected and all
-  output in has been consumed, eg s-->avail_out > 0
-BZ_OK
-  otherwise
-

Allowable next actions:

-
BZ2_bzDecompress
-  if BZ_OK was returned
-BZ2_bzDecompressEnd
-  otherwise
-
-
-

-3.3.6. BZ2_bzDecompressEnd

-
int BZ2_bzDecompressEnd ( bz_stream *strm );
-

Releases all memory associated with a decompression -stream.

-

Possible return values:

-
BZ_PARAM_ERROR
-  if strm is NULL or strm->s is NULL
-BZ_OK
-  otherwise
-

Allowable next actions:

-
  None.
-
-
-
-

-3.4. High-level interface

-

This interface provides functions for reading and writing -bzip2 format files. First, some -general points.

-
    -
  • All of the functions take an - int* first argument, - bzerror. After each call, - bzerror should be consulted - first to determine the outcome of the call. If - bzerror is - BZ_OK, the call completed - successfully, and only then should the return value of the - function (if any) be consulted. If - bzerror is - BZ_IO_ERROR, there was an - error reading/writing the underlying compressed file, and you - should then consult errno / - perror to determine the cause - of the difficulty. bzerror - may also be set to various other values; precise details are - given on a per-function basis below.

  • -
  • If bzerror indicates - an error (ie, anything except - BZ_OK and - BZ_STREAM_END), you should - immediately call - BZ2_bzReadClose (or - BZ2_bzWriteClose, depending on - whether you are attempting to read or to write) to free up all - resources associated with the stream. Once an error has been - indicated, behaviour of all calls except - BZ2_bzReadClose - (BZ2_bzWriteClose) is - undefined. The implication is that (1) - bzerror should be checked - after each call, and (2) if - bzerror indicates an error, - BZ2_bzReadClose - (BZ2_bzWriteClose) should then - be called to clean up.

  • -
  • The FILE* arguments - passed to BZ2_bzReadOpen / - BZ2_bzWriteOpen should be set - to binary mode. Most Unix systems will do this by default, but - other platforms, including Windows and Mac, will not. If you - omit this, you may encounter problems when moving code to new - platforms.

  • -
  • Memory allocation requests are handled by - malloc / - free. At present there is no - facility for user-defined memory allocators in the file I/O - functions (could easily be added, though).

  • -
-
-

-3.4.1. BZ2_bzReadOpen

-
typedef void BZFILE;
-
-BZFILE *BZ2_bzReadOpen( int *bzerror, FILE *f, 
-                        int verbosity, int small,
-                        void *unused, int nUnused );
-

Prepare to read compressed data from file handle -f. -f should refer to a file which -has been opened for reading, and for which the error indicator -(ferror(f))is not set. If -small is 1, the library will try -to decompress using less memory, at the expense of speed.

-

For reasons explained below, -BZ2_bzRead will decompress the -nUnused bytes starting at -unused, before starting to read -from the file f. At most -BZ_MAX_UNUSED bytes may be -supplied like this. If this facility is not required, you should -pass NULL and -0 for -unused and -nUnused respectively.

-

For the meaning of parameters -small and -verbosity, see -BZ2_bzDecompressInit.

-

The amount of memory needed to decompress a file cannot be -determined until the file's header has been read. So it is -possible that BZ2_bzReadOpen -returns BZ_OK but a subsequent -call of BZ2_bzRead will return -BZ_MEM_ERROR.

-

Possible assignments to -bzerror:

-
BZ_CONFIG_ERROR
-  if the library has been mis-compiled
-BZ_PARAM_ERROR
-  if f is NULL
-  or small is neither 0 nor 1
-  or ( unused == NULL && nUnused != 0 )
-  or ( unused != NULL && !(0 <= nUnused <= BZ_MAX_UNUSED) )
-BZ_IO_ERROR
-  if ferror(f) is nonzero
-BZ_MEM_ERROR
-  if insufficient memory is available
-BZ_OK
-  otherwise.
-

Possible return values:

-
Pointer to an abstract BZFILE
-  if bzerror is BZ_OK
-NULL
-  otherwise
-

Allowable next actions:

-
BZ2_bzRead
-  if bzerror is BZ_OK
-BZ2_bzClose
-  otherwise
-
-
-

-3.4.2. BZ2_bzRead

-
int BZ2_bzRead ( int *bzerror, BZFILE *b, void *buf, int len );
-

Reads up to len -(uncompressed) bytes from the compressed file -b into the buffer -buf. If the read was -successful, bzerror is set to -BZ_OK and the number of bytes -read is returned. If the logical end-of-stream was detected, -bzerror will be set to -BZ_STREAM_END, and the number of -bytes read is returned. All other -bzerror values denote an -error.

-

BZ2_bzRead will supply -len bytes, unless the logical -stream end is detected or an error occurs. Because of this, it -is possible to detect the stream end by observing when the number -of bytes returned is less than the number requested. -Nevertheless, this is regarded as inadvisable; you should instead -check bzerror after every call -and watch out for -BZ_STREAM_END.

-

Internally, BZ2_bzRead -copies data from the compressed file in chunks of size -BZ_MAX_UNUSED bytes before -decompressing it. If the file contains more bytes than strictly -needed to reach the logical end-of-stream, -BZ2_bzRead will almost certainly -read some of the trailing data before signalling -BZ_SEQUENCE_END. To collect the -read but unused data once -BZ_SEQUENCE_END has appeared, -call BZ2_bzReadGetUnused -immediately before -BZ2_bzReadClose.

-

Possible assignments to -bzerror:

-
BZ_PARAM_ERROR
-  if b is NULL or buf is NULL or len < 0
-BZ_SEQUENCE_ERROR
-  if b was opened with BZ2_bzWriteOpen
-BZ_IO_ERROR
-  if there is an error reading from the compressed file
-BZ_UNEXPECTED_EOF
-  if the compressed file ended before 
-  the logical end-of-stream was detected
-BZ_DATA_ERROR
-  if a data integrity error was detected in the compressed stream
-BZ_DATA_ERROR_MAGIC
-  if the stream does not begin with the requisite header bytes 
-  (ie, is not a bzip2 data file).  This is really 
-  a special case of BZ_DATA_ERROR.
-BZ_MEM_ERROR
-  if insufficient memory was available
-BZ_STREAM_END
-  if the logical end of stream was detected.
-BZ_OK
-  otherwise.
-

Possible return values:

-
number of bytes read
-  if bzerror is BZ_OK or BZ_STREAM_END
-undefined
-  otherwise
-

Allowable next actions:

-
collect data from buf, then BZ2_bzRead or BZ2_bzReadClose
-  if bzerror is BZ_OK
-collect data from buf, then BZ2_bzReadClose or BZ2_bzReadGetUnused
-  if bzerror is BZ_SEQUENCE_END
-BZ2_bzReadClose
-  otherwise
-
-
-

-3.4.3. BZ2_bzReadGetUnused

-
void BZ2_bzReadGetUnused( int* bzerror, BZFILE *b, 
-                          void** unused, int* nUnused );
-

Returns data which was read from the compressed file but -was not needed to get to the logical end-of-stream. -*unused is set to the address of -the data, and *nUnused to the -number of bytes. *nUnused will -be set to a value between 0 and -BZ_MAX_UNUSED inclusive.

-

This function may only be called once -BZ2_bzRead has signalled -BZ_STREAM_END but before -BZ2_bzReadClose.

-

Possible assignments to -bzerror:

-
BZ_PARAM_ERROR
-  if b is NULL
-  or unused is NULL or nUnused is NULL
-BZ_SEQUENCE_ERROR
-  if BZ_STREAM_END has not been signalled
-  or if b was opened with BZ2_bzWriteOpen
-BZ_OK
-  otherwise
-

Allowable next actions:

-
BZ2_bzReadClose
-
-
-

-3.4.4. BZ2_bzReadClose

-
void BZ2_bzReadClose ( int *bzerror, BZFILE *b );
-

Releases all memory pertaining to the compressed file -b. -BZ2_bzReadClose does not call -fclose on the underlying file -handle, so you should do that yourself if appropriate. -BZ2_bzReadClose should be called -to clean up after all error situations.

-

Possible assignments to -bzerror:

-
BZ_SEQUENCE_ERROR
-  if b was opened with BZ2_bzOpenWrite
-BZ_OK
-  otherwise
-

Allowable next actions:

-
none
-
-
-

-3.4.5. BZ2_bzWriteOpen

-
BZFILE *BZ2_bzWriteOpen( int *bzerror, FILE *f, 
-                         int blockSize100k, int verbosity,
-                         int workFactor );
-

Prepare to write compressed data to file handle -f. -f should refer to a file which -has been opened for writing, and for which the error indicator -(ferror(f))is not set.

-

For the meaning of parameters -blockSize100k, -verbosity and -workFactor, see -BZ2_bzCompressInit.

-

All required memory is allocated at this stage, so if the -call completes successfully, -BZ_MEM_ERROR cannot be signalled -by a subsequent call to -BZ2_bzWrite.

-

Possible assignments to -bzerror:

-
BZ_CONFIG_ERROR
-  if the library has been mis-compiled
-BZ_PARAM_ERROR
-  if f is NULL
-  or blockSize100k < 1 or blockSize100k > 9
-BZ_IO_ERROR
-  if ferror(f) is nonzero
-BZ_MEM_ERROR
-  if insufficient memory is available
-BZ_OK
-  otherwise
-

Possible return values:

-
Pointer to an abstract BZFILE
-  if bzerror is BZ_OK
-NULL
-  otherwise
-

Allowable next actions:

-
BZ2_bzWrite
-  if bzerror is BZ_OK
-  (you could go directly to BZ2_bzWriteClose, but this would be pretty pointless)
-BZ2_bzWriteClose
-  otherwise
-
-
-

-3.4.6. BZ2_bzWrite

-
void BZ2_bzWrite ( int *bzerror, BZFILE *b, void *buf, int len );
-

Absorbs len bytes from the -buffer buf, eventually to be -compressed and written to the file.

-

Possible assignments to -bzerror:

-
BZ_PARAM_ERROR
-  if b is NULL or buf is NULL or len < 0
-BZ_SEQUENCE_ERROR
-  if b was opened with BZ2_bzReadOpen
-BZ_IO_ERROR
-  if there is an error writing the compressed file.
-BZ_OK
-  otherwise
-
-
-

-3.4.7. BZ2_bzWriteClose

-
void BZ2_bzWriteClose( int *bzerror, BZFILE* f,
-                       int abandon,
-                       unsigned int* nbytes_in,
-                       unsigned int* nbytes_out );
-
-void BZ2_bzWriteClose64( int *bzerror, BZFILE* f,
-                         int abandon,
-                         unsigned int* nbytes_in_lo32,
-                         unsigned int* nbytes_in_hi32,
-                         unsigned int* nbytes_out_lo32,
-                         unsigned int* nbytes_out_hi32 );
-

Compresses and flushes to the compressed file all data so -far supplied by BZ2_bzWrite. -The logical end-of-stream markers are also written, so subsequent -calls to BZ2_bzWrite are -illegal. All memory associated with the compressed file -b is released. -fflush is called on the -compressed file, but it is not -fclose'd.

-

If BZ2_bzWriteClose is -called to clean up after an error, the only action is to release -the memory. The library records the error codes issued by -previous calls, so this situation will be detected automatically. -There is no attempt to complete the compression operation, nor to -fflush the compressed file. You -can force this behaviour to happen even in the case of no error, -by passing a nonzero value to -abandon.

-

If nbytes_in is non-null, -*nbytes_in will be set to be the -total volume of uncompressed data handled. Similarly, -nbytes_out will be set to the -total volume of compressed data written. For compatibility with -older versions of the library, -BZ2_bzWriteClose only yields the -lower 32 bits of these counts. Use -BZ2_bzWriteClose64 if you want -the full 64 bit counts. These two functions are otherwise -absolutely identical.

-

Possible assignments to -bzerror:

-
BZ_SEQUENCE_ERROR
-  if b was opened with BZ2_bzReadOpen
-BZ_IO_ERROR
-  if there is an error writing the compressed file
-BZ_OK
-  otherwise
-
-
-

-3.4.8. Handling embedded compressed data streams

-

The high-level library facilitates use of -bzip2 data streams which form -some part of a surrounding, larger data stream.

-
    -
  • For writing, the library takes an open file handle, - writes compressed data to it, - fflushes it but does not - fclose it. The calling - application can write its own data before and after the - compressed data stream, using that same file handle.

  • -
  • Reading is more complex, and the facilities are not as - general as they could be since generality is hard to reconcile - with efficiency. BZ2_bzRead - reads from the compressed file in blocks of size - BZ_MAX_UNUSED bytes, and in - doing so probably will overshoot the logical end of compressed - stream. To recover this data once decompression has ended, - call BZ2_bzReadGetUnused after - the last call of BZ2_bzRead - (the one returning - BZ_STREAM_END) but before - calling - BZ2_bzReadClose.

  • -
-

This mechanism makes it easy to decompress multiple -bzip2 streams placed end-to-end. -As the end of one stream, when -BZ2_bzRead returns -BZ_STREAM_END, call -BZ2_bzReadGetUnused to collect -the unused data (copy it into your own buffer somewhere). That -data forms the start of the next compressed stream. To start -uncompressing that next stream, call -BZ2_bzReadOpen again, feeding in -the unused data via the unused / -nUnused parameters. Keep doing -this until BZ_STREAM_END return -coincides with the physical end of file -(feof(f)). In this situation -BZ2_bzReadGetUnused will of -course return no data.

-

This should give some feel for how the high-level interface -can be used. If you require extra flexibility, you'll have to -bite the bullet and get to grips with the low-level -interface.

-
-
-

-3.4.9. Standard file-reading/writing code

-

Here's how you'd write data to a compressed file:

-
FILE*   f;
-BZFILE* b;
-int     nBuf;
-char    buf[ /* whatever size you like */ ];
-int     bzerror;
-int     nWritten;
-
-f = fopen ( "myfile.bz2", "w" );
-if ( !f ) {
- /* handle error */
-}
-b = BZ2_bzWriteOpen( &bzerror, f, 9 );
-if (bzerror != BZ_OK) {
- BZ2_bzWriteClose ( b );
- /* handle error */
-}
-
-while ( /* condition */ ) {
- /* get data to write into buf, and set nBuf appropriately */
- nWritten = BZ2_bzWrite ( &bzerror, b, buf, nBuf );
- if (bzerror == BZ_IO_ERROR) { 
-   BZ2_bzWriteClose ( &bzerror, b );
-   /* handle error */
- }
-}
-
-BZ2_bzWriteClose( &bzerror, b );
-if (bzerror == BZ_IO_ERROR) {
- /* handle error */
-}
-

And to read from a compressed file:

-
FILE*   f;
-BZFILE* b;
-int     nBuf;
-char    buf[ /* whatever size you like */ ];
-int     bzerror;
-int     nWritten;
-
-f = fopen ( "myfile.bz2", "r" );
-if ( !f ) {
-  /* handle error */
-}
-b = BZ2_bzReadOpen ( &bzerror, f, 0, NULL, 0 );
-if ( bzerror != BZ_OK ) {
-  BZ2_bzReadClose ( &bzerror, b );
-  /* handle error */
-}
-
-bzerror = BZ_OK;
-while ( bzerror == BZ_OK && /* arbitrary other conditions */) {
-  nBuf = BZ2_bzRead ( &bzerror, b, buf, /* size of buf */ );
-  if ( bzerror == BZ_OK ) {
-    /* do something with buf[0 .. nBuf-1] */
-  }
-}
-if ( bzerror != BZ_STREAM_END ) {
-   BZ2_bzReadClose ( &bzerror, b );
-   /* handle error */
-} else {
-   BZ2_bzReadClose ( &bzerror, b );
-}
-
-
-
-

-3.5. Utility functions

-
-

-3.5.1. BZ2_bzBuffToBuffCompress

-
int BZ2_bzBuffToBuffCompress( char*         dest,
-                              unsigned int* destLen,
-                              char*         source,
-                              unsigned int  sourceLen,
-                              int           blockSize100k,
-                              int           verbosity,
-                              int           workFactor );
-

Attempts to compress the data in source[0 -.. sourceLen-1] into the destination buffer, -dest[0 .. *destLen-1]. If the -destination buffer is big enough, -*destLen is set to the size of -the compressed data, and BZ_OK -is returned. If the compressed data won't fit, -*destLen is unchanged, and -BZ_OUTBUFF_FULL is -returned.

-

Compression in this manner is a one-shot event, done with a -single call to this function. The resulting compressed data is a -complete bzip2 format data -stream. There is no mechanism for making additional calls to -provide extra input data. If you want that kind of mechanism, -use the low-level interface.

-

For the meaning of parameters -blockSize100k, -verbosity and -workFactor, see -BZ2_bzCompressInit.

-

To guarantee that the compressed data will fit in its -buffer, allocate an output buffer of size 1% larger than the -uncompressed data, plus six hundred extra bytes.

-

BZ2_bzBuffToBuffDecompress -will not write data at or beyond -dest[*destLen], even in case of -buffer overflow.

-

Possible return values:

-
BZ_CONFIG_ERROR
-  if the library has been mis-compiled
-BZ_PARAM_ERROR
-  if dest is NULL or destLen is NULL
-  or blockSize100k < 1 or blockSize100k > 9
-  or verbosity < 0 or verbosity > 4
-  or workFactor < 0 or workFactor > 250
-BZ_MEM_ERROR
-  if insufficient memory is available 
-BZ_OUTBUFF_FULL
-  if the size of the compressed data exceeds *destLen
-BZ_OK
-  otherwise
-
-
-

-3.5.2. BZ2_bzBuffToBuffDecompress

-
int BZ2_bzBuffToBuffDecompress( char*         dest,
-                                unsigned int* destLen,
-                                char*         source,
-                                unsigned int  sourceLen,
-                                int           small,
-                                int           verbosity );
-

Attempts to decompress the data in source[0 -.. sourceLen-1] into the destination buffer, -dest[0 .. *destLen-1]. If the -destination buffer is big enough, -*destLen is set to the size of -the uncompressed data, and BZ_OK -is returned. If the compressed data won't fit, -*destLen is unchanged, and -BZ_OUTBUFF_FULL is -returned.

-

source is assumed to hold -a complete bzip2 format data -stream. -BZ2_bzBuffToBuffDecompress tries -to decompress the entirety of the stream into the output -buffer.

-

For the meaning of parameters -small and -verbosity, see -BZ2_bzDecompressInit.

-

Because the compression ratio of the compressed data cannot -be known in advance, there is no easy way to guarantee that the -output buffer will be big enough. You may of course make -arrangements in your code to record the size of the uncompressed -data, but such a mechanism is beyond the scope of this -library.

-

BZ2_bzBuffToBuffDecompress -will not write data at or beyond -dest[*destLen], even in case of -buffer overflow.

-

Possible return values:

-
BZ_CONFIG_ERROR
-  if the library has been mis-compiled
-BZ_PARAM_ERROR
-  if dest is NULL or destLen is NULL
-  or small != 0 && small != 1
-  or verbosity < 0 or verbosity > 4
-BZ_MEM_ERROR
-  if insufficient memory is available 
-BZ_OUTBUFF_FULL
-  if the size of the compressed data exceeds *destLen
-BZ_DATA_ERROR
-  if a data integrity error was detected in the compressed data
-BZ_DATA_ERROR_MAGIC
-  if the compressed data doesn't begin with the right magic bytes
-BZ_UNEXPECTED_EOF
-  if the compressed data ends unexpectedly
-BZ_OK
-  otherwise
-
-
-
-

-3.6. zlib compatibility functions

-

Yoshioka Tsuneo has contributed some functions to give -better zlib compatibility. -These functions are BZ2_bzopen, -BZ2_bzread, -BZ2_bzwrite, -BZ2_bzflush, -BZ2_bzclose, -BZ2_bzerror and -BZ2_bzlibVersion. These -functions are not (yet) officially part of the library. If they -break, you get to keep all the pieces. Nevertheless, I think -they work ok.

-
typedef void BZFILE;
-
-const char * BZ2_bzlibVersion ( void );
-

Returns a string indicating the library version.

-
BZFILE * BZ2_bzopen  ( const char *path, const char *mode );
-BZFILE * BZ2_bzdopen ( int        fd,    const char *mode );
-

Opens a .bz2 file for -reading or writing, using either its name or a pre-existing file -descriptor. Analogous to fopen -and fdopen.

-
int BZ2_bzread  ( BZFILE* b, void* buf, int len );
-int BZ2_bzwrite ( BZFILE* b, void* buf, int len );
-

Reads/writes data from/to a previously opened -BZFILE. Analogous to -fread and -fwrite.

-
int  BZ2_bzflush ( BZFILE* b );
-void BZ2_bzclose ( BZFILE* b );
-

Flushes/closes a BZFILE. -BZ2_bzflush doesn't actually do -anything. Analogous to fflush -and fclose.

-
const char * BZ2_bzerror ( BZFILE *b, int *errnum )
-

Returns a string describing the more recent error status of -b, and also sets -*errnum to its numerical -value.

-
-
-

-3.7. Using the library in a stdio-free environment

-
-

-3.7.1. Getting rid of stdio

-

In a deeply embedded application, you might want to use -just the memory-to-memory functions. You can do this -conveniently by compiling the library with preprocessor symbol -BZ_NO_STDIO defined. Doing this -gives you a library containing only the following eight -functions:

-

BZ2_bzCompressInit, -BZ2_bzCompress, -BZ2_bzCompressEnd -BZ2_bzDecompressInit, -BZ2_bzDecompress, -BZ2_bzDecompressEnd -BZ2_bzBuffToBuffCompress, -BZ2_bzBuffToBuffDecompress

-

When compiled like this, all functions will ignore -verbosity settings.

-
-
-

-3.7.2. Critical error handling

-

libbzip2 contains a number -of internal assertion checks which should, needless to say, never -be activated. Nevertheless, if an assertion should fail, -behaviour depends on whether or not the library was compiled with -BZ_NO_STDIO set.

-

For a normal compile, an assertion failure yields the -message:

-
-

bzip2/libbzip2: internal error number N.

-

This is a bug in bzip2/libbzip2, 1.0.5 of 10 December 2007. -Please report it to me at: jseward@bzip.org. If this happened -when you were using some program which uses libbzip2 as a -component, you should also report this bug to the author(s) -of that program. Please make an effort to report this bug; -timely and accurate bug reports eventually lead to higher -quality software. Thanks. Julian Seward, 10 December 2007. -

-
-

where N is some error code -number. If N == 1007, it also -prints some extra text advising the reader that unreliable memory -is often associated with internal error 1007. (This is a -frequently-observed-phenomenon with versions 1.0.0/1.0.1).

-

exit(3) is then -called.

-

For a stdio-free library, -assertion failures result in a call to a function declared -as:

-
extern void bz_internal_error ( int errcode );
-

The relevant code is passed as a parameter. You should -supply such a function.

-

In either case, once an assertion failure has occurred, any -bz_stream records involved can -be regarded as invalid. You should not attempt to resume normal -operation with them.

-

You may, of course, change critical error handling to suit -your needs. As I said above, critical errors indicate bugs in -the library and should not occur. All "normal" error situations -are indicated via error return codes from functions, and can be -recovered from.

-
-
-
-

-3.8. Making a Windows DLL

-

Everything related to Windows has been contributed by -Yoshioka Tsuneo -(tsuneo@rr.iij4u.or.jp), so -you should send your queries to him (but perhaps Cc: me, -jseward@bzip.org).

-

My vague understanding of what to do is: using Visual C++ -5.0, open the project file -libbz2.dsp, and build. That's -all.

-

If you can't open the project file for some reason, make a -new one, naming these files: -blocksort.c, -bzlib.c, -compress.c, -crctable.c, -decompress.c, -huffman.c, -randtable.c and -libbz2.def. You will also need -to name the header files bzlib.h -and bzlib_private.h.

-

If you don't use VC++, you may need to define the -proprocessor symbol -_WIN32.

-

Finally, dlltest.c is a -sample program using the DLL. It has a project file, -dlltest.dsp.

-

If you just want a makefile for Visual C, have a look at -makefile.msc.

-

Be aware that if you compile -bzip2 itself on Win32, you must -set BZ_UNIX to 0 and -BZ_LCCWIN32 to 1, in the file -bzip2.c, before compiling. -Otherwise the resulting binary won't work correctly.

-

I haven't tried any of this stuff myself, but it all looks -plausible.

-
-
-
-

-4. Miscellanea

- -

These are just some random thoughts of mine. Your mileage -may vary.

-
-

-4.1. Limitations of the compressed file format

-

bzip2-1.0.X, -0.9.5 and -0.9.0 use exactly the same file -format as the original version, -bzip2-0.1. This decision was -made in the interests of stability. Creating yet another -incompatible compressed file format would create further -confusion and disruption for users.

-

Nevertheless, this is not a painless decision. Development -work since the release of -bzip2-0.1 in August 1997 has -shown complexities in the file format which slow down -decompression and, in retrospect, are unnecessary. These -are:

-
    -
  • The run-length encoder, which is the first of the - compression transformations, is entirely irrelevant. The - original purpose was to protect the sorting algorithm from the - very worst case input: a string of repeated symbols. But - algorithm steps Q6a and Q6b in the original Burrows-Wheeler - technical report (SRC-124) show how repeats can be handled - without difficulty in block sorting.

  • -
  • -

    The randomisation mechanism doesn't really need to be - there. Udi Manber and Gene Myers published a suffix array - construction algorithm a few years back, which can be employed - to sort any block, no matter how repetitive, in O(N log N) - time. Subsequent work by Kunihiko Sadakane has produced a - derivative O(N (log N)^2) algorithm which usually outperforms - the Manber-Myers algorithm.

    -

    I could have changed to Sadakane's algorithm, but I find - it to be slower than bzip2's - existing algorithm for most inputs, and the randomisation - mechanism protects adequately against bad cases. I didn't - think it was a good tradeoff to make. Partly this is due to - the fact that I was not flooded with email complaints about - bzip2-0.1's performance on - repetitive data, so perhaps it isn't a problem for real - inputs.

    -

    Probably the best long-term solution, and the one I have - incorporated into 0.9.5 and above, is to use the existing - sorting algorithm initially, and fall back to a O(N (log N)^2) - algorithm if the standard algorithm gets into - difficulties.

    -
  • -
  • The compressed file format was never designed to be - handled by a library, and I have had to jump though some hoops - to produce an efficient implementation of decompression. It's - a bit hairy. Try passing - decompress.c through the C - preprocessor and you'll see what I mean. Much of this - complexity could have been avoided if the compressed size of - each block of data was recorded in the data stream.

  • -
  • An Adler-32 checksum, rather than a CRC32 checksum, - would be faster to compute.

  • -
-

It would be fair to say that the -bzip2 format was frozen before I -properly and fully understood the performance consequences of -doing so.

-

Improvements which I was able to incorporate into 0.9.0, -despite using the same file format, are:

-
    -
  • Single array implementation of the inverse BWT. This - significantly speeds up decompression, presumably because it - reduces the number of cache misses.

  • -
  • Faster inverse MTF transform for large MTF values. - The new implementation is based on the notion of sliding blocks - of values.

  • -
  • bzip2-0.9.0 now reads - and writes files with fread - and fwrite; version 0.1 used - putc and - getc. Duh! Well, you live - and learn.

  • -
-

Further ahead, it would be nice to be able to do random -access into files. This will require some careful design of -compressed file formats.

-
-
-

-4.2. Portability issues

-

After some consideration, I have decided not to use GNU -autoconf to configure 0.9.5 or -1.0.

-

autoconf, admirable and -wonderful though it is, mainly assists with portability problems -between Unix-like platforms. But -bzip2 doesn't have much in the -way of portability problems on Unix; most of the difficulties -appear when porting to the Mac, or to Microsoft's operating -systems. autoconf doesn't help -in those cases, and brings in a whole load of new -complexity.

-

Most people should be able to compile the library and -program under Unix straight out-of-the-box, so to speak, -especially if you have a version of GNU C available.

-

There are a couple of -__inline__ directives in the -code. GNU C (gcc) should be -able to handle them. If you're not using GNU C, your C compiler -shouldn't see them at all. If your compiler does, for some -reason, see them and doesn't like them, just -#define -__inline__ to be -/* */. One easy way to do this -is to compile with the flag --D__inline__=, which should be -understood by most Unix compilers.

-

If you still have difficulties, try compiling with the -macro BZ_STRICT_ANSI defined. -This should enable you to build the library in a strictly ANSI -compliant environment. Building the program itself like this is -dangerous and not supported, since you remove -bzip2's checks against -compressing directories, symbolic links, devices, and other -not-really-a-file entities. This could cause filesystem -corruption!

-

One other thing: if you create a -bzip2 binary for public distribution, -please consider linking it statically (gcc --static). This avoids all sorts of library-version -issues that others may encounter later on.

-

If you build bzip2 on -Win32, you must set BZ_UNIX to 0 -and BZ_LCCWIN32 to 1, in the -file bzip2.c, before compiling. -Otherwise the resulting binary won't work correctly.

-
-
-

-4.3. Reporting bugs

-

I tried pretty hard to make sure -bzip2 is bug free, both by -design and by testing. Hopefully you'll never need to read this -section for real.

-

Nevertheless, if bzip2 dies -with a segmentation fault, a bus error or an internal assertion -failure, it will ask you to email me a bug report. Experience from -years of feedback of bzip2 users indicates that almost all these -problems can be traced to either compiler bugs or hardware -problems.

-
    -
  • -

    Recompile the program with no optimisation, and - see if it works. And/or try a different compiler. I heard all - sorts of stories about various flavours of GNU C (and other - compilers) generating bad code for - bzip2, and I've run across two - such examples myself.

    -

    2.7.X versions of GNU C are known to generate bad code - from time to time, at high optimisation levels. If you get - problems, try using the flags - -O2 - -fomit-frame-pointer - -fno-strength-reduce. You - should specifically not use - -funroll-loops.

    -

    You may notice that the Makefile runs six tests as part - of the build process. If the program passes all of these, it's - a pretty good (but not 100%) indication that the compiler has - done its job correctly.

    -
  • -
  • -

    If bzip2 - crashes randomly, and the crashes are not repeatable, you may - have a flaky memory subsystem. - bzip2 really hammers your - memory hierarchy, and if it's a bit marginal, you may get these - problems. Ditto if your disk or I/O subsystem is slowly - failing. Yup, this really does happen.

    -

    Try using a different machine of the same type, and see - if you can repeat the problem.

    -
  • -
  • This isn't really a bug, but ... If - bzip2 tells you your file is - corrupted on decompression, and you obtained the file via FTP, - there is a possibility that you forgot to tell FTP to do a - binary mode transfer. That absolutely will cause the file to - be non-decompressible. You'll have to transfer it - again.

  • -
-

If you've incorporated -libbzip2 into your own program -and are getting problems, please, please, please, check that the -parameters you are passing in calls to the library, are correct, -and in accordance with what the documentation says is allowable. -I have tried to make the library robust against such problems, -but I'm sure I haven't succeeded.

-

Finally, if the above comments don't help, you'll have to -send me a bug report. Now, it's just amazing how many people -will send me a bug report saying something like:

-
bzip2 crashed with segmentation fault on my machine
-

and absolutely nothing else. Needless to say, a such a -report is totally, utterly, completely and -comprehensively 100% useless; a waste of your time, my time, and -net bandwidth. With no details at all, there's no way -I can possibly begin to figure out what the problem is.

-

The rules of the game are: facts, facts, facts. Don't omit -them because "oh, they won't be relevant". At the bare -minimum:

-
Machine type.  Operating system version.  
-Exact version of bzip2 (do bzip2 -V).  
-Exact version of the compiler used.  
-Flags passed to the compiler.
-

However, the most important single thing that will help me -is the file that you were trying to compress or decompress at the -time the problem happened. Without that, my ability to do -anything more than speculate about the cause, is limited.

-
-
-

-4.4. Did you get the right package?

-

bzip2 is a resource hog. -It soaks up large amounts of CPU cycles and memory. Also, it -gives very large latencies. In the worst case, you can feed many -megabytes of uncompressed data into the library before getting -any compressed output, so this probably rules out applications -requiring interactive behaviour.

-

These aren't faults of my implementation, I hope, but more -an intrinsic property of the Burrows-Wheeler transform -(unfortunately). Maybe this isn't what you want.

-

If you want a compressor and/or library which is faster, -uses less memory but gets pretty good compression, and has -minimal latency, consider Jean-loup Gailly's and Mark Adler's -work, zlib-1.2.1 and -gzip-1.2.4. Look for them at -http://www.zlib.org and -http://www.gzip.org -respectively.

-

For something faster and lighter still, you might try Markus F -X J Oberhumer's LZO real-time -compression/decompression library, at -http://www.oberhumer.com/opensource.

-
-
-

-4.5. Further Reading

-

bzip2 is not research -work, in the sense that it doesn't present any new ideas. -Rather, it's an engineering exercise based on existing -ideas.

-

Four documents describe essentially all the ideas behind -bzip2:

-

Michael Burrows and D. J. Wheeler:
-  "A block-sorting lossless data compression algorithm"
-   10th May 1994. 
-   Digital SRC Research Report 124.
-   ftp://ftp.digital.com/pub/DEC/SRC/research-reports/SRC-124.ps.gz
-   If you have trouble finding it, try searching at the
-   New Zealand Digital Library, http://www.nzdl.org.
-
-Daniel S. Hirschberg and Debra A. LeLewer
-  "Efficient Decoding of Prefix Codes"
-   Communications of the ACM, April 1990, Vol 33, Number 4.
-   You might be able to get an electronic copy of this
-   from the ACM Digital Library.
-
-David J. Wheeler
-   Program bred3.c and accompanying document bred3.ps.
-   This contains the idea behind the multi-table Huffman coding scheme.
-   ftp://ftp.cl.cam.ac.uk/users/djw3/
-
-Jon L. Bentley and Robert Sedgewick
-  "Fast Algorithms for Sorting and Searching Strings"
-   Available from Sedgewick's web page,
-   www.cs.princeton.edu/~rs
-

-

The following paper gives valuable additional insights into -the algorithm, but is not immediately the basis of any code used -in bzip2.

-

Peter Fenwick:
-   Block Sorting Text Compression
-   Proceedings of the 19th Australasian Computer Science Conference,
-     Melbourne, Australia.  Jan 31 - Feb 2, 1996.
-   ftp://ftp.cs.auckland.ac.nz/pub/peter-f/ACSC96paper.ps

-

Kunihiko Sadakane's sorting algorithm, mentioned above, is -available from:

-

http://naomi.is.s.u-tokyo.ac.jp/~sada/papers/Sada98b.ps.gz
-

-

The Manber-Myers suffix array construction algorithm is -described in a paper available from:

-

http://www.cs.arizona.edu/people/gene/PAPERS/suffix.ps
-

-

Finally, the following papers document some -investigations I made into the performance of sorting -and decompression algorithms:

-

Julian Seward
-   On the Performance of BWT Sorting Algorithms
-   Proceedings of the IEEE Data Compression Conference 2000
-     Snowbird, Utah.  28-30 March 2000.
-
-Julian Seward
-   Space-time Tradeoffs in the Inverse B-W Transform
-   Proceedings of the IEEE Data Compression Conference 2001
-     Snowbird, Utah.  27-29 March 2001.
-

-
-
-
- diff --git a/Utilities/cmbzip2/manual.pdf b/Utilities/cmbzip2/manual.pdf deleted file mode 100644 index 10c10de7ac4..00000000000 Binary files a/Utilities/cmbzip2/manual.pdf and /dev/null differ diff --git a/Utilities/cmbzip2/manual.ps b/Utilities/cmbzip2/manual.ps deleted file mode 100644 index b8b610c8123..00000000000 Binary files a/Utilities/cmbzip2/manual.ps and /dev/null differ diff --git a/Utilities/cmbzip2/manual.xml b/Utilities/cmbzip2/manual.xml deleted file mode 100644 index f2241360249..00000000000 --- a/Utilities/cmbzip2/manual.xml +++ /dev/null @@ -1,2964 +0,0 @@ - - - %common-ents; -]> - - - - - bzip2 and libbzip2, version 1.0.5 - A program and library for data compression - - &bz-lifespan; - Julian Seward - - Version &bz-version; of &bz-date; - - - - Julian - Seward - - &bz-url; - - - - - - - This program, bzip2, the - associated library libbzip2, and - all documentation, are copyright © &bz-lifespan; Julian Seward. - All rights reserved. - - Redistribution and use in source and binary forms, with - or without modification, are permitted provided that the - following conditions are met: - - - - Redistributions of source code must retain the - above copyright notice, this list of conditions and the - following disclaimer. - - The origin of this software must not be - misrepresented; you must not claim that you wrote the original - software. If you use this software in a product, an - acknowledgment in the product documentation would be - appreciated but is not required. - - Altered source versions must be plainly marked - as such, and must not be misrepresented as being the original - software. - - The name of the author may not be used to - endorse or promote products derived from this software without - specific prior written permission. - - - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. - - PATENTS: To the best of my knowledge, - bzip2 and - libbzip2 do not use any patented - algorithms. However, I do not have the resources to carry - out a patent search. Therefore I cannot give any guarantee of - the above statement. - - - - - - - - - -Introduction - -bzip2 compresses files -using the Burrows-Wheeler block-sorting text compression -algorithm, and Huffman coding. Compression is generally -considerably better than that achieved by more conventional -LZ77/LZ78-based compressors, and approaches the performance of -the PPM family of statistical compressors. - -bzip2 is built on top of -libbzip2, a flexible library for -handling compressed data in the -bzip2 format. This manual -describes both how to use the program and how to work with the -library interface. Most of the manual is devoted to this -library, not the program, which is good news if your interest is -only in the program. - - - - describes how to use - bzip2; this is the only part - you need to read if you just want to know how to operate the - program. - - describes the - programming interfaces in detail, and - - records some - miscellaneous notes which I thought ought to be recorded - somewhere. - - - - - - - -How to use bzip2 - -This chapter contains a copy of the -bzip2 man page, and nothing -else. - - -NAME - - - - bzip2, - bunzip2 - a block-sorting file - compressor, v1.0.4 - - bzcat - - decompresses files to stdout - - bzip2recover - - recovers data from damaged bzip2 files - - - - - - - -SYNOPSIS - - - - bzip2 [ - -cdfkqstvzVL123456789 ] [ filenames ... ] - - bunzip2 [ - -fkvsVL ] [ filenames ... ] - - bzcat [ -s ] [ - filenames ... ] - - bzip2recover - filename - - - - - - - -DESCRIPTION - -bzip2 compresses files -using the Burrows-Wheeler block sorting text compression -algorithm, and Huffman coding. Compression is generally -considerably better than that achieved by more conventional -LZ77/LZ78-based compressors, and approaches the performance of -the PPM family of statistical compressors. - -The command-line options are deliberately very similar to -those of GNU gzip, but they are -not identical. - -bzip2 expects a list of -file names to accompany the command-line flags. Each file is -replaced by a compressed version of itself, with the name -original_name.bz2. Each -compressed file has the same modification date, permissions, and, -when possible, ownership as the corresponding original, so that -these properties can be correctly restored at decompression time. -File name handling is naive in the sense that there is no -mechanism for preserving original file names, permissions, -ownerships or dates in filesystems which lack these concepts, or -have serious file name length restrictions, such as -MS-DOS. - -bzip2 and -bunzip2 will by default not -overwrite existing files. If you want this to happen, specify -the -f flag. - -If no file names are specified, -bzip2 compresses from standard -input to standard output. In this case, -bzip2 will decline to write -compressed output to a terminal, as this would be entirely -incomprehensible and therefore pointless. - -bunzip2 (or -bzip2 -d) decompresses all -specified files. Files which were not created by -bzip2 will be detected and -ignored, and a warning issued. -bzip2 attempts to guess the -filename for the decompressed file from that of the compressed -file as follows: - - - - filename.bz2 - becomes - filename - - filename.bz - becomes - filename - - filename.tbz2 - becomes - filename.tar - - filename.tbz - becomes - filename.tar - - anyothername - becomes - anyothername.out - - - -If the file does not end in one of the recognised endings, -.bz2, -.bz, -.tbz2 or -.tbz, -bzip2 complains that it cannot -guess the name of the original file, and uses the original name -with .out appended. - -As with compression, supplying no filenames causes -decompression from standard input to standard output. - -bunzip2 will correctly -decompress a file which is the concatenation of two or more -compressed files. The result is the concatenation of the -corresponding uncompressed files. Integrity testing -(-t) of concatenated compressed -files is also supported. - -You can also compress or decompress files to the standard -output by giving the -c flag. -Multiple files may be compressed and decompressed like this. The -resulting outputs are fed sequentially to stdout. Compression of -multiple files in this manner generates a stream containing -multiple compressed file representations. Such a stream can be -decompressed correctly only by -bzip2 version 0.9.0 or later. -Earlier versions of bzip2 will -stop after decompressing the first file in the stream. - -bzcat (or -bzip2 -dc) decompresses all -specified files to the standard output. - -bzip2 will read arguments -from the environment variables -BZIP2 and -BZIP, in that order, and will -process them before any arguments read from the command line. -This gives a convenient way to supply default arguments. - -Compression is always performed, even if the compressed -file is slightly larger than the original. Files of less than -about one hundred bytes tend to get larger, since the compression -mechanism has a constant overhead in the region of 50 bytes. -Random data (including the output of most file compressors) is -coded at about 8.05 bits per byte, giving an expansion of around -0.5%. - -As a self-check for your protection, -bzip2 uses 32-bit CRCs to make -sure that the decompressed version of a file is identical to the -original. This guards against corruption of the compressed data, -and against undetected bugs in -bzip2 (hopefully very unlikely). -The chances of data corruption going undetected is microscopic, -about one chance in four billion for each file processed. Be -aware, though, that the check occurs upon decompression, so it -can only tell you that something is wrong. It can't help you -recover the original uncompressed data. You can use -bzip2recover to try to recover -data from damaged files. - -Return values: 0 for a normal exit, 1 for environmental -problems (file not found, invalid flags, I/O errors, etc.), 2 -to indicate a corrupt compressed file, 3 for an internal -consistency error (eg, bug) which caused -bzip2 to panic. - - - - - -OPTIONS - - - - - -c --stdout - Compress or decompress to standard - output. - - - - -d --decompress - Force decompression. - bzip2, - bunzip2 and - bzcat are really the same - program, and the decision about what actions to take is done on - the basis of which name is used. This flag overrides that - mechanism, and forces bzip2 to decompress. - - - - -z --compress - The complement to - -d: forces compression, - regardless of the invokation name. - - - - -t --test - Check integrity of the specified file(s), but - don't decompress them. This really performs a trial - decompression and throws away the result. - - - - -f --force - Force overwrite of output files. Normally, - bzip2 will not overwrite - existing output files. Also forces - bzip2 to break hard links to - files, which it otherwise wouldn't do. - bzip2 normally declines - to decompress files which don't have the correct magic header - bytes. If forced (-f), - however, it will pass such files through unmodified. This is - how GNU gzip behaves. - - - - - -k --keep - Keep (don't delete) input files during - compression or decompression. - - - - -s --small - Reduce memory usage, for compression, - decompression and testing. Files are decompressed and tested - using a modified algorithm which only requires 2.5 bytes per - block byte. This means any file can be decompressed in 2300k - of memory, albeit at about half the normal speed. - During compression, -s - selects a block size of 200k, which limits memory use to around - the same figure, at the expense of your compression ratio. In - short, if your machine is low on memory (8 megabytes or less), - use -s for everything. See - below. - - - - -q --quiet - Suppress non-essential warning messages. - Messages pertaining to I/O errors and other critical events - will not be suppressed. - - - - -v --verbose - Verbose mode -- show the compression ratio for - each file processed. Further - -v's increase the verbosity - level, spewing out lots of information which is primarily of - interest for diagnostic purposes. - - - - -L --license -V --version - Display the software version, license terms and - conditions. - - - - -1 (or - --fast) to - -9 (or - -best) - Set the block size to 100 k, 200 k ... 900 k - when compressing. Has no effect when decompressing. See below. The - --fast and - --best aliases are primarily - for GNU gzip compatibility. - In particular, --fast doesn't - make things significantly faster. And - --best merely selects the - default behaviour. - - - - -- - Treats all subsequent arguments as file names, - even if they start with a dash. This is so you can handle - files with names beginning with a dash, for example: - bzip2 -- - -myfilename. - - - - --repetitive-fast - --repetitive-best - These flags are redundant in versions 0.9.5 and - above. They provided some coarse control over the behaviour of - the sorting algorithm in earlier versions, which was sometimes - useful. 0.9.5 and above have an improved algorithm which - renders these flags irrelevant. - - - - - - - - -MEMORY MANAGEMENT - -bzip2 compresses large -files in blocks. The block size affects both the compression -ratio achieved, and the amount of memory needed for compression -and decompression. The flags -1 -through -9 specify the block -size to be 100,000 bytes through 900,000 bytes (the default) -respectively. At decompression time, the block size used for -compression is read from the header of the compressed file, and -bunzip2 then allocates itself -just enough memory to decompress the file. Since block sizes are -stored in compressed files, it follows that the flags --1 to --9 are irrelevant to and so -ignored during decompression. - -Compression and decompression requirements, in bytes, can be -estimated as: - -Compression: 400k + ( 8 x block size ) - -Decompression: 100k + ( 4 x block size ), or - 100k + ( 2.5 x block size ) - - -Larger block sizes give rapidly diminishing marginal -returns. Most of the compression comes from the first two or -three hundred k of block size, a fact worth bearing in mind when -using bzip2 on small machines. -It is also important to appreciate that the decompression memory -requirement is set at compression time by the choice of block -size. - -For files compressed with the default 900k block size, -bunzip2 will require about 3700 -kbytes to decompress. To support decompression of any file on a -4 megabyte machine, bunzip2 has -an option to decompress using approximately half this amount of -memory, about 2300 kbytes. Decompression speed is also halved, -so you should use this option only where necessary. The relevant -flag is -s. - -In general, try and use the largest block size memory -constraints allow, since that maximises the compression achieved. -Compression and decompression speed are virtually unaffected by -block size. - -Another significant point applies to files which fit in a -single block -- that means most files you'd encounter using a -large block size. The amount of real memory touched is -proportional to the size of the file, since the file is smaller -than a block. For example, compressing a file 20,000 bytes long -with the flag -9 will cause the -compressor to allocate around 7600k of memory, but only touch -400k + 20000 * 8 = 560 kbytes of it. Similarly, the decompressor -will allocate 3700k but only touch 100k + 20000 * 4 = 180 -kbytes. - -Here is a table which summarises the maximum memory usage -for different block sizes. Also recorded is the total compressed -size for 14 files of the Calgary Text Compression Corpus -totalling 3,141,622 bytes. This column gives some feel for how -compression varies with block size. These figures tend to -understate the advantage of larger block sizes for larger files, -since the Corpus is dominated by smaller files. - - - Compress Decompress Decompress Corpus -Flag usage usage -s usage Size - - -1 1200k 500k 350k 914704 - -2 2000k 900k 600k 877703 - -3 2800k 1300k 850k 860338 - -4 3600k 1700k 1100k 846899 - -5 4400k 2100k 1350k 845160 - -6 5200k 2500k 1600k 838626 - -7 6100k 2900k 1850k 834096 - -8 6800k 3300k 2100k 828642 - -9 7600k 3700k 2350k 828642 - - - - - - -RECOVERING DATA FROM DAMAGED FILES - -bzip2 compresses files in -blocks, usually 900kbytes long. Each block is handled -independently. If a media or transmission error causes a -multi-block .bz2 file to become -damaged, it may be possible to recover data from the undamaged -blocks in the file. - -The compressed representation of each block is delimited by -a 48-bit pattern, which makes it possible to find the block -boundaries with reasonable certainty. Each block also carries -its own 32-bit CRC, so damaged blocks can be distinguished from -undamaged ones. - -bzip2recover is a simple -program whose purpose is to search for blocks in -.bz2 files, and write each block -out into its own .bz2 file. You -can then use bzip2 -t to test -the integrity of the resulting files, and decompress those which -are undamaged. - -bzip2recover takes a -single argument, the name of the damaged file, and writes a -number of files rec0001file.bz2, -rec0002file.bz2, etc, containing -the extracted blocks. The output filenames are designed so that -the use of wildcards in subsequent processing -- for example, -bzip2 -dc rec*file.bz2 > -recovered_data -- lists the files in the correct -order. - -bzip2recover should be of -most use dealing with large .bz2 -files, as these will contain many blocks. It is clearly futile -to use it on damaged single-block files, since a damaged block -cannot be recovered. If you wish to minimise any potential data -loss through media or transmission errors, you might consider -compressing with a smaller block size. - - - - - -PERFORMANCE NOTES - -The sorting phase of compression gathers together similar -strings in the file. Because of this, files containing very long -runs of repeated symbols, like "aabaabaabaab ..." (repeated -several hundred times) may compress more slowly than normal. -Versions 0.9.5 and above fare much better than previous versions -in this respect. The ratio between worst-case and average-case -compression time is in the region of 10:1. For previous -versions, this figure was more like 100:1. You can use the --vvvv option to monitor progress -in great detail, if you want. - -Decompression speed is unaffected by these -phenomena. - -bzip2 usually allocates -several megabytes of memory to operate in, and then charges all -over it in a fairly random fashion. This means that performance, -both for compressing and decompressing, is largely determined by -the speed at which your machine can service cache misses. -Because of this, small changes to the code to reduce the miss -rate have been observed to give disproportionately large -performance improvements. I imagine -bzip2 will perform best on -machines with very large caches. - - - - - - -CAVEATS - -I/O error messages are not as helpful as they could be. -bzip2 tries hard to detect I/O -errors and exit cleanly, but the details of what the problem is -sometimes seem rather misleading. - -This manual page pertains to version &bz-version; of -bzip2. Compressed data created by -this version is entirely forwards and backwards compatible with the -previous public releases, versions 0.1pl2, 0.9.0 and 0.9.5, 1.0.0, -1.0.1, 1.0.2 and 1.0.3, but with the following exception: 0.9.0 and -above can correctly decompress multiple concatenated compressed files. -0.1pl2 cannot do this; it will stop after decompressing just the first -file in the stream. - -bzip2recover versions -prior to 1.0.2 used 32-bit integers to represent bit positions in -compressed files, so it could not handle compressed files more -than 512 megabytes long. Versions 1.0.2 and above use 64-bit ints -on some platforms which support them (GNU supported targets, and -Windows). To establish whether or not -bzip2recover was built with such -a limitation, run it without arguments. In any event you can -build yourself an unlimited version if you can recompile it with -MaybeUInt64 set to be an -unsigned 64-bit integer. - - - - - - -AUTHOR - -Julian Seward, -&bz-email; - -The ideas embodied in -bzip2 are due to (at least) the -following people: Michael Burrows and David Wheeler (for the -block sorting transformation), David Wheeler (again, for the -Huffman coder), Peter Fenwick (for the structured coding model in -the original bzip, and many -refinements), and Alistair Moffat, Radford Neal and Ian Witten -(for the arithmetic coder in the original -bzip). I am much indebted for -their help, support and advice. See the manual in the source -distribution for pointers to sources of documentation. Christian -von Roques encouraged me to look for faster sorting algorithms, -so as to speed up compression. Bela Lubkin encouraged me to -improve the worst-case compression performance. -Donna Robinson XMLised the documentation. -Many people sent -patches, helped with portability problems, lent machines, gave -advice and were generally helpful. - - - - - - - - - -Programming with <computeroutput>libbzip2</computeroutput> - - -This chapter describes the programming interface to -libbzip2. - -For general background information, particularly about -memory use and performance aspects, you'd be well advised to read - as well. - - - -Top-level structure - -libbzip2 is a flexible -library for compressing and decompressing data in the -bzip2 data format. Although -packaged as a single entity, it helps to regard the library as -three separate parts: the low level interface, and the high level -interface, and some utility functions. - -The structure of -libbzip2's interfaces is similar -to that of Jean-loup Gailly's and Mark Adler's excellent -zlib library. - -All externally visible symbols have names beginning -BZ2_. This is new in version -1.0. The intention is to minimise pollution of the namespaces of -library clients. - -To use any part of the library, you need to -#include <bzlib.h> -into your sources. - - - - -Low-level summary - -This interface provides services for compressing and -decompressing data in memory. There's no provision for dealing -with files, streams or any other I/O mechanisms, just straight -memory-to-memory work. In fact, this part of the library can be -compiled without inclusion of -stdio.h, which may be helpful -for embedded applications. - -The low-level part of the library has no global variables -and is therefore thread-safe. - -Six routines make up the low level interface: -BZ2_bzCompressInit, -BZ2_bzCompress, and -BZ2_bzCompressEnd for -compression, and a corresponding trio -BZ2_bzDecompressInit, -BZ2_bzDecompress and -BZ2_bzDecompressEnd for -decompression. The *Init -functions allocate memory for compression/decompression and do -other initialisations, whilst the -*End functions close down -operations and release memory. - -The real work is done by -BZ2_bzCompress and -BZ2_bzDecompress. These -compress and decompress data from a user-supplied input buffer to -a user-supplied output buffer. These buffers can be any size; -arbitrary quantities of data are handled by making repeated calls -to these functions. This is a flexible mechanism allowing a -consumer-pull style of activity, or producer-push, or a mixture -of both. - - - - - -High-level summary - -This interface provides some handy wrappers around the -low-level interface to facilitate reading and writing -bzip2 format files -(.bz2 files). The routines -provide hooks to facilitate reading files in which the -bzip2 data stream is embedded -within some larger-scale file structure, or where there are -multiple bzip2 data streams -concatenated end-to-end. - -For reading files, -BZ2_bzReadOpen, -BZ2_bzRead, -BZ2_bzReadClose and -BZ2_bzReadGetUnused are -supplied. For writing files, -BZ2_bzWriteOpen, -BZ2_bzWrite and -BZ2_bzWriteFinish are -available. - -As with the low-level library, no global variables are used -so the library is per se thread-safe. However, if I/O errors -occur whilst reading or writing the underlying compressed files, -you may have to consult errno to -determine the cause of the error. In that case, you'd need a C -library which correctly supports -errno in a multithreaded -environment. - -To make the library a little simpler and more portable, -BZ2_bzReadOpen and -BZ2_bzWriteOpen require you to -pass them file handles (FILE*s) -which have previously been opened for reading or writing -respectively. That avoids portability problems associated with -file operations and file attributes, whilst not being much of an -imposition on the programmer. - - - - - -Utility functions summary - -For very simple needs, -BZ2_bzBuffToBuffCompress and -BZ2_bzBuffToBuffDecompress are -provided. These compress data in memory from one buffer to -another buffer in a single function call. You should assess -whether these functions fulfill your memory-to-memory -compression/decompression requirements before investing effort in -understanding the more general but more complex low-level -interface. - -Yoshioka Tsuneo -(tsuneo@rr.iij4u.or.jp) has -contributed some functions to give better -zlib compatibility. These -functions are BZ2_bzopen, -BZ2_bzread, -BZ2_bzwrite, -BZ2_bzflush, -BZ2_bzclose, -BZ2_bzerror and -BZ2_bzlibVersion. You may find -these functions more convenient for simple file reading and -writing, than those in the high-level interface. These functions -are not (yet) officially part of the library, and are minimally -documented here. If they break, you get to keep all the pieces. -I hope to document them properly when time permits. - -Yoshioka also contributed modifications to allow the -library to be built as a Windows DLL. - - - - - - - -Error handling - -The library is designed to recover cleanly in all -situations, including the worst-case situation of decompressing -random data. I'm not 100% sure that it can always do this, so -you might want to add a signal handler to catch segmentation -violations during decompression if you are feeling especially -paranoid. I would be interested in hearing more about the -robustness of the library to corrupted compressed data. - -Version 1.0.3 more robust in this respect than any -previous version. Investigations with Valgrind (a tool for detecting -problems with memory management) indicate -that, at least for the few files I tested, all single-bit errors -in the decompressed data are caught properly, with no -segmentation faults, no uses of uninitialised data, no out of -range reads or writes, and no infinite looping in the decompressor. -So it's certainly pretty robust, although -I wouldn't claim it to be totally bombproof. - -The file bzlib.h contains -all definitions needed to use the library. In particular, you -should definitely not include -bzlib_private.h. - -In bzlib.h, the various -return values are defined. The following list is not intended as -an exhaustive description of the circumstances in which a given -value may be returned -- those descriptions are given later. -Rather, it is intended to convey the rough meaning of each return -value. The first five actions are normal and not intended to -denote an error situation. - - - - - BZ_OK - The requested action was completed - successfully. - - - - BZ_RUN_OK, BZ_FLUSH_OK, - BZ_FINISH_OK - In - BZ2_bzCompress, the requested - flush/finish/nothing-special action was completed - successfully. - - - - BZ_STREAM_END - Compression of data was completed, or the - logical stream end was detected during - decompression. - - - - -The following return values indicate an error of some -kind. - - - - - BZ_CONFIG_ERROR - Indicates that the library has been improperly - compiled on your platform -- a major configuration error. - Specifically, it means that - sizeof(char), - sizeof(short) and - sizeof(int) are not 1, 2 and - 4 respectively, as they should be. Note that the library - should still work properly on 64-bit platforms which follow - the LP64 programming model -- that is, where - sizeof(long) and - sizeof(void*) are 8. Under - LP64, sizeof(int) is still 4, - so libbzip2, which doesn't - use the long type, is - OK. - - - - BZ_SEQUENCE_ERROR - When using the library, it is important to call - the functions in the correct sequence and with data structures - (buffers etc) in the correct states. - libbzip2 checks as much as it - can to ensure this is happening, and returns - BZ_SEQUENCE_ERROR if not. - Code which complies precisely with the function semantics, as - detailed below, should never receive this value; such an event - denotes buggy code which you should - investigate. - - - - BZ_PARAM_ERROR - Returned when a parameter to a function call is - out of range or otherwise manifestly incorrect. As with - BZ_SEQUENCE_ERROR, this - denotes a bug in the client code. The distinction between - BZ_PARAM_ERROR and - BZ_SEQUENCE_ERROR is a bit - hazy, but still worth making. - - - - BZ_MEM_ERROR - Returned when a request to allocate memory - failed. Note that the quantity of memory needed to decompress - a stream cannot be determined until the stream's header has - been read. So - BZ2_bzDecompress and - BZ2_bzRead may return - BZ_MEM_ERROR even though some - of the compressed data has been read. The same is not true - for compression; once - BZ2_bzCompressInit or - BZ2_bzWriteOpen have - successfully completed, - BZ_MEM_ERROR cannot - occur. - - - - BZ_DATA_ERROR - Returned when a data integrity error is - detected during decompression. Most importantly, this means - when stored and computed CRCs for the data do not match. This - value is also returned upon detection of any other anomaly in - the compressed data. - - - - BZ_DATA_ERROR_MAGIC - As a special case of - BZ_DATA_ERROR, it is - sometimes useful to know when the compressed stream does not - start with the correct magic bytes ('B' 'Z' - 'h'). - - - - BZ_IO_ERROR - Returned by - BZ2_bzRead and - BZ2_bzWrite when there is an - error reading or writing in the compressed file, and by - BZ2_bzReadOpen and - BZ2_bzWriteOpen for attempts - to use a file for which the error indicator (viz, - ferror(f)) is set. On - receipt of BZ_IO_ERROR, the - caller should consult errno - and/or perror to acquire - operating-system specific information about the - problem. - - - - BZ_UNEXPECTED_EOF - Returned by - BZ2_bzRead when the - compressed file finishes before the logical end of stream is - detected. - - - - BZ_OUTBUFF_FULL - Returned by - BZ2_bzBuffToBuffCompress and - BZ2_bzBuffToBuffDecompress to - indicate that the output data will not fit into the output - buffer provided. - - - - - - - - - -Low-level interface - - - -<computeroutput>BZ2_bzCompressInit</computeroutput> - - -typedef struct { - char *next_in; - unsigned int avail_in; - unsigned int total_in_lo32; - unsigned int total_in_hi32; - - char *next_out; - unsigned int avail_out; - unsigned int total_out_lo32; - unsigned int total_out_hi32; - - void *state; - - void *(*bzalloc)(void *,int,int); - void (*bzfree)(void *,void *); - void *opaque; -} bz_stream; - -int BZ2_bzCompressInit ( bz_stream *strm, - int blockSize100k, - int verbosity, - int workFactor ); - - -Prepares for compression. The -bz_stream structure holds all -data pertaining to the compression activity. A -bz_stream structure should be -allocated and initialised prior to the call. The fields of -bz_stream comprise the entirety -of the user-visible data. state -is a pointer to the private data structures required for -compression. - -Custom memory allocators are supported, via fields -bzalloc, -bzfree, and -opaque. The value -opaque is passed to as the first -argument to all calls to bzalloc -and bzfree, but is otherwise -ignored by the library. The call bzalloc ( -opaque, n, m ) is expected to return a pointer -p to n * -m bytes of memory, and bzfree ( -opaque, p ) should free that memory. - -If you don't want to use a custom memory allocator, set -bzalloc, -bzfree and -opaque to -NULL, and the library will then -use the standard malloc / -free routines. - -Before calling -BZ2_bzCompressInit, fields -bzalloc, -bzfree and -opaque should be filled -appropriately, as just described. Upon return, the internal -state will have been allocated and initialised, and -total_in_lo32, -total_in_hi32, -total_out_lo32 and -total_out_hi32 will have been -set to zero. These four fields are used by the library to inform -the caller of the total amount of data passed into and out of the -library, respectively. You should not try to change them. As of -version 1.0, 64-bit counts are maintained, even on 32-bit -platforms, using the _hi32 -fields to store the upper 32 bits of the count. So, for example, -the total amount of data in is (total_in_hi32 -<< 32) + total_in_lo32. - -Parameter blockSize100k -specifies the block size to be used for compression. It should -be a value between 1 and 9 inclusive, and the actual block size -used is 100000 x this figure. 9 gives the best compression but -takes most memory. - -Parameter verbosity should -be set to a number between 0 and 4 inclusive. 0 is silent, and -greater numbers give increasingly verbose monitoring/debugging -output. If the library has been compiled with --DBZ_NO_STDIO, no such output -will appear for any verbosity setting. - -Parameter workFactor -controls how the compression phase behaves when presented with -worst case, highly repetitive, input data. If compression runs -into difficulties caused by repetitive data, the library switches -from the standard sorting algorithm to a fallback algorithm. The -fallback is slower than the standard algorithm by perhaps a -factor of three, but always behaves reasonably, no matter how bad -the input. - -Lower values of workFactor -reduce the amount of effort the standard algorithm will expend -before resorting to the fallback. You should set this parameter -carefully; too low, and many inputs will be handled by the -fallback algorithm and so compress rather slowly, too high, and -your average-to-worst case compression times can become very -large. The default value of 30 gives reasonable behaviour over a -wide range of circumstances. - -Allowable values range from 0 to 250 inclusive. 0 is a -special case, equivalent to using the default value of 30. - -Note that the compressed output generated is the same -regardless of whether or not the fallback algorithm is -used. - -Be aware also that this parameter may disappear entirely in -future versions of the library. In principle it should be -possible to devise a good way to automatically choose which -algorithm to use. Such a mechanism would render the parameter -obsolete. - -Possible return values: - - -BZ_CONFIG_ERROR - if the library has been mis-compiled -BZ_PARAM_ERROR - if strm is NULL - or blockSize < 1 or blockSize > 9 - or verbosity < 0 or verbosity > 4 - or workFactor < 0 or workFactor > 250 -BZ_MEM_ERROR - if not enough memory is available -BZ_OK - otherwise - - -Allowable next actions: - - -BZ2_bzCompress - if BZ_OK is returned - no specific action needed in case of error - - - - - - -<computeroutput>BZ2_bzCompress</computeroutput> - - -int BZ2_bzCompress ( bz_stream *strm, int action ); - - -Provides more input and/or output buffer space for the -library. The caller maintains input and output buffers, and -calls BZ2_bzCompress to transfer -data between them. - -Before each call to -BZ2_bzCompress, -next_in should point at the data -to be compressed, and avail_in -should indicate how many bytes the library may read. -BZ2_bzCompress updates -next_in, -avail_in and -total_in to reflect the number -of bytes it has read. - -Similarly, next_out should -point to a buffer in which the compressed data is to be placed, -with avail_out indicating how -much output space is available. -BZ2_bzCompress updates -next_out, -avail_out and -total_out to reflect the number -of bytes output. - -You may provide and remove as little or as much data as you -like on each call of -BZ2_bzCompress. In the limit, -it is acceptable to supply and remove data one byte at a time, -although this would be terribly inefficient. You should always -ensure that at least one byte of output space is available at -each call. - -A second purpose of -BZ2_bzCompress is to request a -change of mode of the compressed stream. - -Conceptually, a compressed stream can be in one of four -states: IDLE, RUNNING, FLUSHING and FINISHING. Before -initialisation -(BZ2_bzCompressInit) and after -termination (BZ2_bzCompressEnd), -a stream is regarded as IDLE. - -Upon initialisation -(BZ2_bzCompressInit), the stream -is placed in the RUNNING state. Subsequent calls to -BZ2_bzCompress should pass -BZ_RUN as the requested action; -other actions are illegal and will result in -BZ_SEQUENCE_ERROR. - -At some point, the calling program will have provided all -the input data it wants to. It will then want to finish up -- in -effect, asking the library to process any data it might have -buffered internally. In this state, -BZ2_bzCompress will no longer -attempt to read data from -next_in, but it will want to -write data to next_out. Because -the output buffer supplied by the user can be arbitrarily small, -the finishing-up operation cannot necessarily be done with a -single call of -BZ2_bzCompress. - -Instead, the calling program passes -BZ_FINISH as an action to -BZ2_bzCompress. This changes -the stream's state to FINISHING. Any remaining input (ie, -next_in[0 .. avail_in-1]) is -compressed and transferred to the output buffer. To do this, -BZ2_bzCompress must be called -repeatedly until all the output has been consumed. At that -point, BZ2_bzCompress returns -BZ_STREAM_END, and the stream's -state is set back to IDLE. -BZ2_bzCompressEnd should then be -called. - -Just to make sure the calling program does not cheat, the -library makes a note of avail_in -at the time of the first call to -BZ2_bzCompress which has -BZ_FINISH as an action (ie, at -the time the program has announced its intention to not supply -any more input). By comparing this value with that of -avail_in over subsequent calls -to BZ2_bzCompress, the library -can detect any attempts to slip in more data to compress. Any -calls for which this is detected will return -BZ_SEQUENCE_ERROR. This -indicates a programming mistake which should be corrected. - -Instead of asking to finish, the calling program may ask -BZ2_bzCompress to take all the -remaining input, compress it and terminate the current -(Burrows-Wheeler) compression block. This could be useful for -error control purposes. The mechanism is analogous to that for -finishing: call BZ2_bzCompress -with an action of BZ_FLUSH, -remove output data, and persist with the -BZ_FLUSH action until the value -BZ_RUN is returned. As with -finishing, BZ2_bzCompress -detects any attempt to provide more input data once the flush has -begun. - -Once the flush is complete, the stream returns to the -normal RUNNING state. - -This all sounds pretty complex, but isn't really. Here's a -table which shows which actions are allowable in each state, what -action will be taken, what the next state is, and what the -non-error return values are. Note that you can't explicitly ask -what state the stream is in, but nor do you need to -- it can be -inferred from the values returned by -BZ2_bzCompress. - - -IDLE/any - Illegal. IDLE state only exists after BZ2_bzCompressEnd or - before BZ2_bzCompressInit. - Return value = BZ_SEQUENCE_ERROR - -RUNNING/BZ_RUN - Compress from next_in to next_out as much as possible. - Next state = RUNNING - Return value = BZ_RUN_OK - -RUNNING/BZ_FLUSH - Remember current value of next_in. Compress from next_in - to next_out as much as possible, but do not accept any more input. - Next state = FLUSHING - Return value = BZ_FLUSH_OK - -RUNNING/BZ_FINISH - Remember current value of next_in. Compress from next_in - to next_out as much as possible, but do not accept any more input. - Next state = FINISHING - Return value = BZ_FINISH_OK - -FLUSHING/BZ_FLUSH - Compress from next_in to next_out as much as possible, - but do not accept any more input. - If all the existing input has been used up and all compressed - output has been removed - Next state = RUNNING; Return value = BZ_RUN_OK - else - Next state = FLUSHING; Return value = BZ_FLUSH_OK - -FLUSHING/other - Illegal. - Return value = BZ_SEQUENCE_ERROR - -FINISHING/BZ_FINISH - Compress from next_in to next_out as much as possible, - but to not accept any more input. - If all the existing input has been used up and all compressed - output has been removed - Next state = IDLE; Return value = BZ_STREAM_END - else - Next state = FINISHING; Return value = BZ_FINISH_OK - -FINISHING/other - Illegal. - Return value = BZ_SEQUENCE_ERROR - - - -That still looks complicated? Well, fair enough. The -usual sequence of calls for compressing a load of data is: - - - - Get started with - BZ2_bzCompressInit. - - Shovel data in and shlurp out its compressed form - using zero or more calls of - BZ2_bzCompress with action = - BZ_RUN. - - Finish up. Repeatedly call - BZ2_bzCompress with action = - BZ_FINISH, copying out the - compressed output, until - BZ_STREAM_END is - returned. Close up and go home. Call - BZ2_bzCompressEnd. - - - -If the data you want to compress fits into your input -buffer all at once, you can skip the calls of -BZ2_bzCompress ( ..., BZ_RUN ) -and just do the BZ2_bzCompress ( ..., BZ_FINISH -) calls. - -All required memory is allocated by -BZ2_bzCompressInit. The -compression library can accept any data at all (obviously). So -you shouldn't get any error return values from the -BZ2_bzCompress calls. If you -do, they will be -BZ_SEQUENCE_ERROR, and indicate -a bug in your programming. - -Trivial other possible return values: - - -BZ_PARAM_ERROR - if strm is NULL, or strm->s is NULL - - - - - - -<computeroutput>BZ2_bzCompressEnd</computeroutput> - - -int BZ2_bzCompressEnd ( bz_stream *strm ); - - -Releases all memory associated with a compression -stream. - -Possible return values: - - -BZ_PARAM_ERROR if strm is NULL or strm->s is NULL -BZ_OK otherwise - - - - - - -<computeroutput>BZ2_bzDecompressInit</computeroutput> - - -int BZ2_bzDecompressInit ( bz_stream *strm, int verbosity, int small ); - - -Prepares for decompression. As with -BZ2_bzCompressInit, a -bz_stream record should be -allocated and initialised before the call. Fields -bzalloc, -bzfree and -opaque should be set if a custom -memory allocator is required, or made -NULL for the normal -malloc / -free routines. Upon return, the -internal state will have been initialised, and -total_in and -total_out will be zero. - -For the meaning of parameter -verbosity, see -BZ2_bzCompressInit. - -If small is nonzero, the -library will use an alternative decompression algorithm which -uses less memory but at the cost of decompressing more slowly -(roughly speaking, half the speed, but the maximum memory -requirement drops to around 2300k). See -for more information on memory management. - -Note that the amount of memory needed to decompress a -stream cannot be determined until the stream's header has been -read, so even if -BZ2_bzDecompressInit succeeds, a -subsequent BZ2_bzDecompress -could fail with -BZ_MEM_ERROR. - -Possible return values: - - -BZ_CONFIG_ERROR - if the library has been mis-compiled -BZ_PARAM_ERROR - if ( small != 0 && small != 1 ) - or (verbosity <; 0 || verbosity > 4) -BZ_MEM_ERROR - if insufficient memory is available - - -Allowable next actions: - - -BZ2_bzDecompress - if BZ_OK was returned - no specific action required in case of error - - - - - - -<computeroutput>BZ2_bzDecompress</computeroutput> - - -int BZ2_bzDecompress ( bz_stream *strm ); - - -Provides more input and/out output buffer space for the -library. The caller maintains input and output buffers, and uses -BZ2_bzDecompress to transfer -data between them. - -Before each call to -BZ2_bzDecompress, -next_in should point at the -compressed data, and avail_in -should indicate how many bytes the library may read. -BZ2_bzDecompress updates -next_in, -avail_in and -total_in to reflect the number -of bytes it has read. - -Similarly, next_out should -point to a buffer in which the uncompressed output is to be -placed, with avail_out -indicating how much output space is available. -BZ2_bzCompress updates -next_out, -avail_out and -total_out to reflect the number -of bytes output. - -You may provide and remove as little or as much data as you -like on each call of -BZ2_bzDecompress. In the limit, -it is acceptable to supply and remove data one byte at a time, -although this would be terribly inefficient. You should always -ensure that at least one byte of output space is available at -each call. - -Use of BZ2_bzDecompress is -simpler than -BZ2_bzCompress. - -You should provide input and remove output as described -above, and repeatedly call -BZ2_bzDecompress until -BZ_STREAM_END is returned. -Appearance of BZ_STREAM_END -denotes that BZ2_bzDecompress -has detected the logical end of the compressed stream. -BZ2_bzDecompress will not -produce BZ_STREAM_END until all -output data has been placed into the output buffer, so once -BZ_STREAM_END appears, you are -guaranteed to have available all the decompressed output, and -BZ2_bzDecompressEnd can safely -be called. - -If case of an error return value, you should call -BZ2_bzDecompressEnd to clean up -and release memory. - -Possible return values: - - -BZ_PARAM_ERROR - if strm is NULL or strm->s is NULL - or strm->avail_out < 1 -BZ_DATA_ERROR - if a data integrity error is detected in the compressed stream -BZ_DATA_ERROR_MAGIC - if the compressed stream doesn't begin with the right magic bytes -BZ_MEM_ERROR - if there wasn't enough memory available -BZ_STREAM_END - if the logical end of the data stream was detected and all - output in has been consumed, eg s-->avail_out > 0 -BZ_OK - otherwise - - -Allowable next actions: - - -BZ2_bzDecompress - if BZ_OK was returned -BZ2_bzDecompressEnd - otherwise - - - - - - -<computeroutput>BZ2_bzDecompressEnd</computeroutput> - - -int BZ2_bzDecompressEnd ( bz_stream *strm ); - - -Releases all memory associated with a decompression -stream. - -Possible return values: - - -BZ_PARAM_ERROR - if strm is NULL or strm->s is NULL -BZ_OK - otherwise - - -Allowable next actions: - - - None. - - - - - - - - -High-level interface - -This interface provides functions for reading and writing -bzip2 format files. First, some -general points. - - - - All of the functions take an - int* first argument, - bzerror. After each call, - bzerror should be consulted - first to determine the outcome of the call. If - bzerror is - BZ_OK, the call completed - successfully, and only then should the return value of the - function (if any) be consulted. If - bzerror is - BZ_IO_ERROR, there was an - error reading/writing the underlying compressed file, and you - should then consult errno / - perror to determine the cause - of the difficulty. bzerror - may also be set to various other values; precise details are - given on a per-function basis below. - - If bzerror indicates - an error (ie, anything except - BZ_OK and - BZ_STREAM_END), you should - immediately call - BZ2_bzReadClose (or - BZ2_bzWriteClose, depending on - whether you are attempting to read or to write) to free up all - resources associated with the stream. Once an error has been - indicated, behaviour of all calls except - BZ2_bzReadClose - (BZ2_bzWriteClose) is - undefined. The implication is that (1) - bzerror should be checked - after each call, and (2) if - bzerror indicates an error, - BZ2_bzReadClose - (BZ2_bzWriteClose) should then - be called to clean up. - - The FILE* arguments - passed to BZ2_bzReadOpen / - BZ2_bzWriteOpen should be set - to binary mode. Most Unix systems will do this by default, but - other platforms, including Windows and Mac, will not. If you - omit this, you may encounter problems when moving code to new - platforms. - - Memory allocation requests are handled by - malloc / - free. At present there is no - facility for user-defined memory allocators in the file I/O - functions (could easily be added, though). - - - - - - -<computeroutput>BZ2_bzReadOpen</computeroutput> - - -typedef void BZFILE; - -BZFILE *BZ2_bzReadOpen( int *bzerror, FILE *f, - int verbosity, int small, - void *unused, int nUnused ); - - -Prepare to read compressed data from file handle -f. -f should refer to a file which -has been opened for reading, and for which the error indicator -(ferror(f))is not set. If -small is 1, the library will try -to decompress using less memory, at the expense of speed. - -For reasons explained below, -BZ2_bzRead will decompress the -nUnused bytes starting at -unused, before starting to read -from the file f. At most -BZ_MAX_UNUSED bytes may be -supplied like this. If this facility is not required, you should -pass NULL and -0 for -unused and -nUnused respectively. - -For the meaning of parameters -small and -verbosity, see -BZ2_bzDecompressInit. - -The amount of memory needed to decompress a file cannot be -determined until the file's header has been read. So it is -possible that BZ2_bzReadOpen -returns BZ_OK but a subsequent -call of BZ2_bzRead will return -BZ_MEM_ERROR. - -Possible assignments to -bzerror: - - -BZ_CONFIG_ERROR - if the library has been mis-compiled -BZ_PARAM_ERROR - if f is NULL - or small is neither 0 nor 1 - or ( unused == NULL && nUnused != 0 ) - or ( unused != NULL && !(0 <= nUnused <= BZ_MAX_UNUSED) ) -BZ_IO_ERROR - if ferror(f) is nonzero -BZ_MEM_ERROR - if insufficient memory is available -BZ_OK - otherwise. - - -Possible return values: - - -Pointer to an abstract BZFILE - if bzerror is BZ_OK -NULL - otherwise - - -Allowable next actions: - - -BZ2_bzRead - if bzerror is BZ_OK -BZ2_bzClose - otherwise - - - - - - -<computeroutput>BZ2_bzRead</computeroutput> - - -int BZ2_bzRead ( int *bzerror, BZFILE *b, void *buf, int len ); - - -Reads up to len -(uncompressed) bytes from the compressed file -b into the buffer -buf. If the read was -successful, bzerror is set to -BZ_OK and the number of bytes -read is returned. If the logical end-of-stream was detected, -bzerror will be set to -BZ_STREAM_END, and the number of -bytes read is returned. All other -bzerror values denote an -error. - -BZ2_bzRead will supply -len bytes, unless the logical -stream end is detected or an error occurs. Because of this, it -is possible to detect the stream end by observing when the number -of bytes returned is less than the number requested. -Nevertheless, this is regarded as inadvisable; you should instead -check bzerror after every call -and watch out for -BZ_STREAM_END. - -Internally, BZ2_bzRead -copies data from the compressed file in chunks of size -BZ_MAX_UNUSED bytes before -decompressing it. If the file contains more bytes than strictly -needed to reach the logical end-of-stream, -BZ2_bzRead will almost certainly -read some of the trailing data before signalling -BZ_SEQUENCE_END. To collect the -read but unused data once -BZ_SEQUENCE_END has appeared, -call BZ2_bzReadGetUnused -immediately before -BZ2_bzReadClose. - -Possible assignments to -bzerror: - - -BZ_PARAM_ERROR - if b is NULL or buf is NULL or len < 0 -BZ_SEQUENCE_ERROR - if b was opened with BZ2_bzWriteOpen -BZ_IO_ERROR - if there is an error reading from the compressed file -BZ_UNEXPECTED_EOF - if the compressed file ended before - the logical end-of-stream was detected -BZ_DATA_ERROR - if a data integrity error was detected in the compressed stream -BZ_DATA_ERROR_MAGIC - if the stream does not begin with the requisite header bytes - (ie, is not a bzip2 data file). This is really - a special case of BZ_DATA_ERROR. -BZ_MEM_ERROR - if insufficient memory was available -BZ_STREAM_END - if the logical end of stream was detected. -BZ_OK - otherwise. - - -Possible return values: - - -number of bytes read - if bzerror is BZ_OK or BZ_STREAM_END -undefined - otherwise - - -Allowable next actions: - - -collect data from buf, then BZ2_bzRead or BZ2_bzReadClose - if bzerror is BZ_OK -collect data from buf, then BZ2_bzReadClose or BZ2_bzReadGetUnused - if bzerror is BZ_SEQUENCE_END -BZ2_bzReadClose - otherwise - - - - - - -<computeroutput>BZ2_bzReadGetUnused</computeroutput> - - -void BZ2_bzReadGetUnused( int* bzerror, BZFILE *b, - void** unused, int* nUnused ); - - -Returns data which was read from the compressed file but -was not needed to get to the logical end-of-stream. -*unused is set to the address of -the data, and *nUnused to the -number of bytes. *nUnused will -be set to a value between 0 and -BZ_MAX_UNUSED inclusive. - -This function may only be called once -BZ2_bzRead has signalled -BZ_STREAM_END but before -BZ2_bzReadClose. - -Possible assignments to -bzerror: - - -BZ_PARAM_ERROR - if b is NULL - or unused is NULL or nUnused is NULL -BZ_SEQUENCE_ERROR - if BZ_STREAM_END has not been signalled - or if b was opened with BZ2_bzWriteOpen -BZ_OK - otherwise - - -Allowable next actions: - - -BZ2_bzReadClose - - - - - - -<computeroutput>BZ2_bzReadClose</computeroutput> - - -void BZ2_bzReadClose ( int *bzerror, BZFILE *b ); - - -Releases all memory pertaining to the compressed file -b. -BZ2_bzReadClose does not call -fclose on the underlying file -handle, so you should do that yourself if appropriate. -BZ2_bzReadClose should be called -to clean up after all error situations. - -Possible assignments to -bzerror: - - -BZ_SEQUENCE_ERROR - if b was opened with BZ2_bzOpenWrite -BZ_OK - otherwise - - -Allowable next actions: - - -none - - - - - - -<computeroutput>BZ2_bzWriteOpen</computeroutput> - - -BZFILE *BZ2_bzWriteOpen( int *bzerror, FILE *f, - int blockSize100k, int verbosity, - int workFactor ); - - -Prepare to write compressed data to file handle -f. -f should refer to a file which -has been opened for writing, and for which the error indicator -(ferror(f))is not set. - -For the meaning of parameters -blockSize100k, -verbosity and -workFactor, see -BZ2_bzCompressInit. - -All required memory is allocated at this stage, so if the -call completes successfully, -BZ_MEM_ERROR cannot be signalled -by a subsequent call to -BZ2_bzWrite. - -Possible assignments to -bzerror: - - -BZ_CONFIG_ERROR - if the library has been mis-compiled -BZ_PARAM_ERROR - if f is NULL - or blockSize100k < 1 or blockSize100k > 9 -BZ_IO_ERROR - if ferror(f) is nonzero -BZ_MEM_ERROR - if insufficient memory is available -BZ_OK - otherwise - - -Possible return values: - - -Pointer to an abstract BZFILE - if bzerror is BZ_OK -NULL - otherwise - - -Allowable next actions: - - -BZ2_bzWrite - if bzerror is BZ_OK - (you could go directly to BZ2_bzWriteClose, but this would be pretty pointless) -BZ2_bzWriteClose - otherwise - - - - - - -<computeroutput>BZ2_bzWrite</computeroutput> - - -void BZ2_bzWrite ( int *bzerror, BZFILE *b, void *buf, int len ); - - -Absorbs len bytes from the -buffer buf, eventually to be -compressed and written to the file. - -Possible assignments to -bzerror: - - -BZ_PARAM_ERROR - if b is NULL or buf is NULL or len < 0 -BZ_SEQUENCE_ERROR - if b was opened with BZ2_bzReadOpen -BZ_IO_ERROR - if there is an error writing the compressed file. -BZ_OK - otherwise - - - - - - -<computeroutput>BZ2_bzWriteClose</computeroutput> - - -void BZ2_bzWriteClose( int *bzerror, BZFILE* f, - int abandon, - unsigned int* nbytes_in, - unsigned int* nbytes_out ); - -void BZ2_bzWriteClose64( int *bzerror, BZFILE* f, - int abandon, - unsigned int* nbytes_in_lo32, - unsigned int* nbytes_in_hi32, - unsigned int* nbytes_out_lo32, - unsigned int* nbytes_out_hi32 ); - - -Compresses and flushes to the compressed file all data so -far supplied by BZ2_bzWrite. -The logical end-of-stream markers are also written, so subsequent -calls to BZ2_bzWrite are -illegal. All memory associated with the compressed file -b is released. -fflush is called on the -compressed file, but it is not -fclose'd. - -If BZ2_bzWriteClose is -called to clean up after an error, the only action is to release -the memory. The library records the error codes issued by -previous calls, so this situation will be detected automatically. -There is no attempt to complete the compression operation, nor to -fflush the compressed file. You -can force this behaviour to happen even in the case of no error, -by passing a nonzero value to -abandon. - -If nbytes_in is non-null, -*nbytes_in will be set to be the -total volume of uncompressed data handled. Similarly, -nbytes_out will be set to the -total volume of compressed data written. For compatibility with -older versions of the library, -BZ2_bzWriteClose only yields the -lower 32 bits of these counts. Use -BZ2_bzWriteClose64 if you want -the full 64 bit counts. These two functions are otherwise -absolutely identical. - -Possible assignments to -bzerror: - - -BZ_SEQUENCE_ERROR - if b was opened with BZ2_bzReadOpen -BZ_IO_ERROR - if there is an error writing the compressed file -BZ_OK - otherwise - - - - - - -Handling embedded compressed data streams - -The high-level library facilitates use of -bzip2 data streams which form -some part of a surrounding, larger data stream. - - - - For writing, the library takes an open file handle, - writes compressed data to it, - fflushes it but does not - fclose it. The calling - application can write its own data before and after the - compressed data stream, using that same file handle. - - Reading is more complex, and the facilities are not as - general as they could be since generality is hard to reconcile - with efficiency. BZ2_bzRead - reads from the compressed file in blocks of size - BZ_MAX_UNUSED bytes, and in - doing so probably will overshoot the logical end of compressed - stream. To recover this data once decompression has ended, - call BZ2_bzReadGetUnused after - the last call of BZ2_bzRead - (the one returning - BZ_STREAM_END) but before - calling - BZ2_bzReadClose. - - - -This mechanism makes it easy to decompress multiple -bzip2 streams placed end-to-end. -As the end of one stream, when -BZ2_bzRead returns -BZ_STREAM_END, call -BZ2_bzReadGetUnused to collect -the unused data (copy it into your own buffer somewhere). That -data forms the start of the next compressed stream. To start -uncompressing that next stream, call -BZ2_bzReadOpen again, feeding in -the unused data via the unused / -nUnused parameters. Keep doing -this until BZ_STREAM_END return -coincides with the physical end of file -(feof(f)). In this situation -BZ2_bzReadGetUnused will of -course return no data. - -This should give some feel for how the high-level interface -can be used. If you require extra flexibility, you'll have to -bite the bullet and get to grips with the low-level -interface. - - - - - -Standard file-reading/writing code - -Here's how you'd write data to a compressed file: - - -FILE* f; -BZFILE* b; -int nBuf; -char buf[ /* whatever size you like */ ]; -int bzerror; -int nWritten; - -f = fopen ( "myfile.bz2", "w" ); -if ( !f ) { - /* handle error */ -} -b = BZ2_bzWriteOpen( &bzerror, f, 9 ); -if (bzerror != BZ_OK) { - BZ2_bzWriteClose ( b ); - /* handle error */ -} - -while ( /* condition */ ) { - /* get data to write into buf, and set nBuf appropriately */ - nWritten = BZ2_bzWrite ( &bzerror, b, buf, nBuf ); - if (bzerror == BZ_IO_ERROR) { - BZ2_bzWriteClose ( &bzerror, b ); - /* handle error */ - } -} - -BZ2_bzWriteClose( &bzerror, b ); -if (bzerror == BZ_IO_ERROR) { - /* handle error */ -} - - -And to read from a compressed file: - - -FILE* f; -BZFILE* b; -int nBuf; -char buf[ /* whatever size you like */ ]; -int bzerror; -int nWritten; - -f = fopen ( "myfile.bz2", "r" ); -if ( !f ) { - /* handle error */ -} -b = BZ2_bzReadOpen ( &bzerror, f, 0, NULL, 0 ); -if ( bzerror != BZ_OK ) { - BZ2_bzReadClose ( &bzerror, b ); - /* handle error */ -} - -bzerror = BZ_OK; -while ( bzerror == BZ_OK && /* arbitrary other conditions */) { - nBuf = BZ2_bzRead ( &bzerror, b, buf, /* size of buf */ ); - if ( bzerror == BZ_OK ) { - /* do something with buf[0 .. nBuf-1] */ - } -} -if ( bzerror != BZ_STREAM_END ) { - BZ2_bzReadClose ( &bzerror, b ); - /* handle error */ -} else { - BZ2_bzReadClose ( &bzerror, b ); -} - - - - - - - - -Utility functions - - - -<computeroutput>BZ2_bzBuffToBuffCompress</computeroutput> - - -int BZ2_bzBuffToBuffCompress( char* dest, - unsigned int* destLen, - char* source, - unsigned int sourceLen, - int blockSize100k, - int verbosity, - int workFactor ); - - -Attempts to compress the data in source[0 -.. sourceLen-1] into the destination buffer, -dest[0 .. *destLen-1]. If the -destination buffer is big enough, -*destLen is set to the size of -the compressed data, and BZ_OK -is returned. If the compressed data won't fit, -*destLen is unchanged, and -BZ_OUTBUFF_FULL is -returned. - -Compression in this manner is a one-shot event, done with a -single call to this function. The resulting compressed data is a -complete bzip2 format data -stream. There is no mechanism for making additional calls to -provide extra input data. If you want that kind of mechanism, -use the low-level interface. - -For the meaning of parameters -blockSize100k, -verbosity and -workFactor, see -BZ2_bzCompressInit. - -To guarantee that the compressed data will fit in its -buffer, allocate an output buffer of size 1% larger than the -uncompressed data, plus six hundred extra bytes. - -BZ2_bzBuffToBuffDecompress -will not write data at or beyond -dest[*destLen], even in case of -buffer overflow. - -Possible return values: - - -BZ_CONFIG_ERROR - if the library has been mis-compiled -BZ_PARAM_ERROR - if dest is NULL or destLen is NULL - or blockSize100k < 1 or blockSize100k > 9 - or verbosity < 0 or verbosity > 4 - or workFactor < 0 or workFactor > 250 -BZ_MEM_ERROR - if insufficient memory is available -BZ_OUTBUFF_FULL - if the size of the compressed data exceeds *destLen -BZ_OK - otherwise - - - - - - -<computeroutput>BZ2_bzBuffToBuffDecompress</computeroutput> - - -int BZ2_bzBuffToBuffDecompress( char* dest, - unsigned int* destLen, - char* source, - unsigned int sourceLen, - int small, - int verbosity ); - - -Attempts to decompress the data in source[0 -.. sourceLen-1] into the destination buffer, -dest[0 .. *destLen-1]. If the -destination buffer is big enough, -*destLen is set to the size of -the uncompressed data, and BZ_OK -is returned. If the compressed data won't fit, -*destLen is unchanged, and -BZ_OUTBUFF_FULL is -returned. - -source is assumed to hold -a complete bzip2 format data -stream. -BZ2_bzBuffToBuffDecompress tries -to decompress the entirety of the stream into the output -buffer. - -For the meaning of parameters -small and -verbosity, see -BZ2_bzDecompressInit. - -Because the compression ratio of the compressed data cannot -be known in advance, there is no easy way to guarantee that the -output buffer will be big enough. You may of course make -arrangements in your code to record the size of the uncompressed -data, but such a mechanism is beyond the scope of this -library. - -BZ2_bzBuffToBuffDecompress -will not write data at or beyond -dest[*destLen], even in case of -buffer overflow. - -Possible return values: - - -BZ_CONFIG_ERROR - if the library has been mis-compiled -BZ_PARAM_ERROR - if dest is NULL or destLen is NULL - or small != 0 && small != 1 - or verbosity < 0 or verbosity > 4 -BZ_MEM_ERROR - if insufficient memory is available -BZ_OUTBUFF_FULL - if the size of the compressed data exceeds *destLen -BZ_DATA_ERROR - if a data integrity error was detected in the compressed data -BZ_DATA_ERROR_MAGIC - if the compressed data doesn't begin with the right magic bytes -BZ_UNEXPECTED_EOF - if the compressed data ends unexpectedly -BZ_OK - otherwise - - - - - - - - -<computeroutput>zlib</computeroutput> compatibility functions - -Yoshioka Tsuneo has contributed some functions to give -better zlib compatibility. -These functions are BZ2_bzopen, -BZ2_bzread, -BZ2_bzwrite, -BZ2_bzflush, -BZ2_bzclose, -BZ2_bzerror and -BZ2_bzlibVersion. These -functions are not (yet) officially part of the library. If they -break, you get to keep all the pieces. Nevertheless, I think -they work ok. - - -typedef void BZFILE; - -const char * BZ2_bzlibVersion ( void ); - - -Returns a string indicating the library version. - - -BZFILE * BZ2_bzopen ( const char *path, const char *mode ); -BZFILE * BZ2_bzdopen ( int fd, const char *mode ); - - -Opens a .bz2 file for -reading or writing, using either its name or a pre-existing file -descriptor. Analogous to fopen -and fdopen. - - -int BZ2_bzread ( BZFILE* b, void* buf, int len ); -int BZ2_bzwrite ( BZFILE* b, void* buf, int len ); - - -Reads/writes data from/to a previously opened -BZFILE. Analogous to -fread and -fwrite. - - -int BZ2_bzflush ( BZFILE* b ); -void BZ2_bzclose ( BZFILE* b ); - - -Flushes/closes a BZFILE. -BZ2_bzflush doesn't actually do -anything. Analogous to fflush -and fclose. - - -const char * BZ2_bzerror ( BZFILE *b, int *errnum ) - - -Returns a string describing the more recent error status of -b, and also sets -*errnum to its numerical -value. - - - - - -Using the library in a <computeroutput>stdio</computeroutput>-free environment - - - -Getting rid of <computeroutput>stdio</computeroutput> - -In a deeply embedded application, you might want to use -just the memory-to-memory functions. You can do this -conveniently by compiling the library with preprocessor symbol -BZ_NO_STDIO defined. Doing this -gives you a library containing only the following eight -functions: - -BZ2_bzCompressInit, -BZ2_bzCompress, -BZ2_bzCompressEnd -BZ2_bzDecompressInit, -BZ2_bzDecompress, -BZ2_bzDecompressEnd -BZ2_bzBuffToBuffCompress, -BZ2_bzBuffToBuffDecompress - -When compiled like this, all functions will ignore -verbosity settings. - - - - - -Critical error handling - -libbzip2 contains a number -of internal assertion checks which should, needless to say, never -be activated. Nevertheless, if an assertion should fail, -behaviour depends on whether or not the library was compiled with -BZ_NO_STDIO set. - -For a normal compile, an assertion failure yields the -message: - -
-bzip2/libbzip2: internal error number N. -This is a bug in bzip2/libbzip2, &bz-version; of &bz-date;. -Please report it to me at: &bz-email;. If this happened -when you were using some program which uses libbzip2 as a -component, you should also report this bug to the author(s) -of that program. Please make an effort to report this bug; -timely and accurate bug reports eventually lead to higher -quality software. Thanks. Julian Seward, &bz-date;. -
- -where N is some error code -number. If N == 1007, it also -prints some extra text advising the reader that unreliable memory -is often associated with internal error 1007. (This is a -frequently-observed-phenomenon with versions 1.0.0/1.0.1). - -exit(3) is then -called. - -For a stdio-free library, -assertion failures result in a call to a function declared -as: - - -extern void bz_internal_error ( int errcode ); - - -The relevant code is passed as a parameter. You should -supply such a function. - -In either case, once an assertion failure has occurred, any -bz_stream records involved can -be regarded as invalid. You should not attempt to resume normal -operation with them. - -You may, of course, change critical error handling to suit -your needs. As I said above, critical errors indicate bugs in -the library and should not occur. All "normal" error situations -are indicated via error return codes from functions, and can be -recovered from. - -
- -
- - - -Making a Windows DLL - -Everything related to Windows has been contributed by -Yoshioka Tsuneo -(tsuneo@rr.iij4u.or.jp), so -you should send your queries to him (but perhaps Cc: me, -&bz-email;). - -My vague understanding of what to do is: using Visual C++ -5.0, open the project file -libbz2.dsp, and build. That's -all. - -If you can't open the project file for some reason, make a -new one, naming these files: -blocksort.c, -bzlib.c, -compress.c, -crctable.c, -decompress.c, -huffman.c, -randtable.c and -libbz2.def. You will also need -to name the header files bzlib.h -and bzlib_private.h. - -If you don't use VC++, you may need to define the -proprocessor symbol -_WIN32. - -Finally, dlltest.c is a -sample program using the DLL. It has a project file, -dlltest.dsp. - -If you just want a makefile for Visual C, have a look at -makefile.msc. - -Be aware that if you compile -bzip2 itself on Win32, you must -set BZ_UNIX to 0 and -BZ_LCCWIN32 to 1, in the file -bzip2.c, before compiling. -Otherwise the resulting binary won't work correctly. - -I haven't tried any of this stuff myself, but it all looks -plausible. - - - -
- - - - -Miscellanea - -These are just some random thoughts of mine. Your mileage -may vary. - - - -Limitations of the compressed file format - -bzip2-1.0.X, -0.9.5 and -0.9.0 use exactly the same file -format as the original version, -bzip2-0.1. This decision was -made in the interests of stability. Creating yet another -incompatible compressed file format would create further -confusion and disruption for users. - -Nevertheless, this is not a painless decision. Development -work since the release of -bzip2-0.1 in August 1997 has -shown complexities in the file format which slow down -decompression and, in retrospect, are unnecessary. These -are: - - - - The run-length encoder, which is the first of the - compression transformations, is entirely irrelevant. The - original purpose was to protect the sorting algorithm from the - very worst case input: a string of repeated symbols. But - algorithm steps Q6a and Q6b in the original Burrows-Wheeler - technical report (SRC-124) show how repeats can be handled - without difficulty in block sorting. - - The randomisation mechanism doesn't really need to be - there. Udi Manber and Gene Myers published a suffix array - construction algorithm a few years back, which can be employed - to sort any block, no matter how repetitive, in O(N log N) - time. Subsequent work by Kunihiko Sadakane has produced a - derivative O(N (log N)^2) algorithm which usually outperforms - the Manber-Myers algorithm. - - I could have changed to Sadakane's algorithm, but I find - it to be slower than bzip2's - existing algorithm for most inputs, and the randomisation - mechanism protects adequately against bad cases. I didn't - think it was a good tradeoff to make. Partly this is due to - the fact that I was not flooded with email complaints about - bzip2-0.1's performance on - repetitive data, so perhaps it isn't a problem for real - inputs. - - Probably the best long-term solution, and the one I have - incorporated into 0.9.5 and above, is to use the existing - sorting algorithm initially, and fall back to a O(N (log N)^2) - algorithm if the standard algorithm gets into - difficulties. - - The compressed file format was never designed to be - handled by a library, and I have had to jump though some hoops - to produce an efficient implementation of decompression. It's - a bit hairy. Try passing - decompress.c through the C - preprocessor and you'll see what I mean. Much of this - complexity could have been avoided if the compressed size of - each block of data was recorded in the data stream. - - An Adler-32 checksum, rather than a CRC32 checksum, - would be faster to compute. - - - -It would be fair to say that the -bzip2 format was frozen before I -properly and fully understood the performance consequences of -doing so. - -Improvements which I was able to incorporate into 0.9.0, -despite using the same file format, are: - - - - Single array implementation of the inverse BWT. This - significantly speeds up decompression, presumably because it - reduces the number of cache misses. - - Faster inverse MTF transform for large MTF values. - The new implementation is based on the notion of sliding blocks - of values. - - bzip2-0.9.0 now reads - and writes files with fread - and fwrite; version 0.1 used - putc and - getc. Duh! Well, you live - and learn. - - - -Further ahead, it would be nice to be able to do random -access into files. This will require some careful design of -compressed file formats. - - - - - -Portability issues - -After some consideration, I have decided not to use GNU -autoconf to configure 0.9.5 or -1.0. - -autoconf, admirable and -wonderful though it is, mainly assists with portability problems -between Unix-like platforms. But -bzip2 doesn't have much in the -way of portability problems on Unix; most of the difficulties -appear when porting to the Mac, or to Microsoft's operating -systems. autoconf doesn't help -in those cases, and brings in a whole load of new -complexity. - -Most people should be able to compile the library and -program under Unix straight out-of-the-box, so to speak, -especially if you have a version of GNU C available. - -There are a couple of -__inline__ directives in the -code. GNU C (gcc) should be -able to handle them. If you're not using GNU C, your C compiler -shouldn't see them at all. If your compiler does, for some -reason, see them and doesn't like them, just -#define -__inline__ to be -/* */. One easy way to do this -is to compile with the flag --D__inline__=, which should be -understood by most Unix compilers. - -If you still have difficulties, try compiling with the -macro BZ_STRICT_ANSI defined. -This should enable you to build the library in a strictly ANSI -compliant environment. Building the program itself like this is -dangerous and not supported, since you remove -bzip2's checks against -compressing directories, symbolic links, devices, and other -not-really-a-file entities. This could cause filesystem -corruption! - -One other thing: if you create a -bzip2 binary for public distribution, -please consider linking it statically (gcc --static). This avoids all sorts of library-version -issues that others may encounter later on. - -If you build bzip2 on -Win32, you must set BZ_UNIX to 0 -and BZ_LCCWIN32 to 1, in the -file bzip2.c, before compiling. -Otherwise the resulting binary won't work correctly. - - - - - -Reporting bugs - -I tried pretty hard to make sure -bzip2 is bug free, both by -design and by testing. Hopefully you'll never need to read this -section for real. - -Nevertheless, if bzip2 dies -with a segmentation fault, a bus error or an internal assertion -failure, it will ask you to email me a bug report. Experience from -years of feedback of bzip2 users indicates that almost all these -problems can be traced to either compiler bugs or hardware -problems. - - - - Recompile the program with no optimisation, and - see if it works. And/or try a different compiler. I heard all - sorts of stories about various flavours of GNU C (and other - compilers) generating bad code for - bzip2, and I've run across two - such examples myself. - - 2.7.X versions of GNU C are known to generate bad code - from time to time, at high optimisation levels. If you get - problems, try using the flags - -O2 - -fomit-frame-pointer - -fno-strength-reduce. You - should specifically not use - -funroll-loops. - - You may notice that the Makefile runs six tests as part - of the build process. If the program passes all of these, it's - a pretty good (but not 100%) indication that the compiler has - done its job correctly. - - If bzip2 - crashes randomly, and the crashes are not repeatable, you may - have a flaky memory subsystem. - bzip2 really hammers your - memory hierarchy, and if it's a bit marginal, you may get these - problems. Ditto if your disk or I/O subsystem is slowly - failing. Yup, this really does happen. - - Try using a different machine of the same type, and see - if you can repeat the problem. - - This isn't really a bug, but ... If - bzip2 tells you your file is - corrupted on decompression, and you obtained the file via FTP, - there is a possibility that you forgot to tell FTP to do a - binary mode transfer. That absolutely will cause the file to - be non-decompressible. You'll have to transfer it - again. - - - -If you've incorporated -libbzip2 into your own program -and are getting problems, please, please, please, check that the -parameters you are passing in calls to the library, are correct, -and in accordance with what the documentation says is allowable. -I have tried to make the library robust against such problems, -but I'm sure I haven't succeeded. - -Finally, if the above comments don't help, you'll have to -send me a bug report. Now, it's just amazing how many people -will send me a bug report saying something like: - - -bzip2 crashed with segmentation fault on my machine - - -and absolutely nothing else. Needless to say, a such a -report is totally, utterly, completely and -comprehensively 100% useless; a waste of your time, my time, and -net bandwidth. With no details at all, there's no way -I can possibly begin to figure out what the problem is. - -The rules of the game are: facts, facts, facts. Don't omit -them because "oh, they won't be relevant". At the bare -minimum: - - -Machine type. Operating system version. -Exact version of bzip2 (do bzip2 -V). -Exact version of the compiler used. -Flags passed to the compiler. - - -However, the most important single thing that will help me -is the file that you were trying to compress or decompress at the -time the problem happened. Without that, my ability to do -anything more than speculate about the cause, is limited. - - - - - -Did you get the right package? - -bzip2 is a resource hog. -It soaks up large amounts of CPU cycles and memory. Also, it -gives very large latencies. In the worst case, you can feed many -megabytes of uncompressed data into the library before getting -any compressed output, so this probably rules out applications -requiring interactive behaviour. - -These aren't faults of my implementation, I hope, but more -an intrinsic property of the Burrows-Wheeler transform -(unfortunately). Maybe this isn't what you want. - -If you want a compressor and/or library which is faster, -uses less memory but gets pretty good compression, and has -minimal latency, consider Jean-loup Gailly's and Mark Adler's -work, zlib-1.2.1 and -gzip-1.2.4. Look for them at -http://www.zlib.org and -http://www.gzip.org -respectively. - -For something faster and lighter still, you might try Markus F -X J Oberhumer's LZO real-time -compression/decompression library, at -http://www.oberhumer.com/opensource. - - - - - - -Further Reading - -bzip2 is not research -work, in the sense that it doesn't present any new ideas. -Rather, it's an engineering exercise based on existing -ideas. - -Four documents describe essentially all the ideas behind -bzip2: - -Michael Burrows and D. J. Wheeler: - "A block-sorting lossless data compression algorithm" - 10th May 1994. - Digital SRC Research Report 124. - ftp://ftp.digital.com/pub/DEC/SRC/research-reports/SRC-124.ps.gz - If you have trouble finding it, try searching at the - New Zealand Digital Library, http://www.nzdl.org. - -Daniel S. Hirschberg and Debra A. LeLewer - "Efficient Decoding of Prefix Codes" - Communications of the ACM, April 1990, Vol 33, Number 4. - You might be able to get an electronic copy of this - from the ACM Digital Library. - -David J. Wheeler - Program bred3.c and accompanying document bred3.ps. - This contains the idea behind the multi-table Huffman coding scheme. - ftp://ftp.cl.cam.ac.uk/users/djw3/ - -Jon L. Bentley and Robert Sedgewick - "Fast Algorithms for Sorting and Searching Strings" - Available from Sedgewick's web page, - www.cs.princeton.edu/~rs - - -The following paper gives valuable additional insights into -the algorithm, but is not immediately the basis of any code used -in bzip2. - -Peter Fenwick: - Block Sorting Text Compression - Proceedings of the 19th Australasian Computer Science Conference, - Melbourne, Australia. Jan 31 - Feb 2, 1996. - ftp://ftp.cs.auckland.ac.nz/pub/peter-f/ACSC96paper.ps - -Kunihiko Sadakane's sorting algorithm, mentioned above, is -available from: - -http://naomi.is.s.u-tokyo.ac.jp/~sada/papers/Sada98b.ps.gz - - -The Manber-Myers suffix array construction algorithm is -described in a paper available from: - -http://www.cs.arizona.edu/people/gene/PAPERS/suffix.ps - - -Finally, the following papers document some -investigations I made into the performance of sorting -and decompression algorithms: - -Julian Seward - On the Performance of BWT Sorting Algorithms - Proceedings of the IEEE Data Compression Conference 2000 - Snowbird, Utah. 28-30 March 2000. - -Julian Seward - Space-time Tradeoffs in the Inverse B-W Transform - Proceedings of the IEEE Data Compression Conference 2001 - Snowbird, Utah. 27-29 March 2001. - - - - - - -
diff --git a/Utilities/cmbzip2/mk251.c b/Utilities/cmbzip2/mk251.c index 39e94c05735..6c5bbf9350e 100644 --- a/Utilities/cmbzip2/mk251.c +++ b/Utilities/cmbzip2/mk251.c @@ -9,8 +9,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. diff --git a/Utilities/cmbzip2/randtable.c b/Utilities/cmbzip2/randtable.c index 068b76367bc..bdc6d4a4cc9 100644 --- a/Utilities/cmbzip2/randtable.c +++ b/Utilities/cmbzip2/randtable.c @@ -8,8 +8,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. diff --git a/Utilities/cmbzip2/sample1.rb2 b/Utilities/cmbzip2/sample1.rb2 deleted file mode 100644 index 4edda362a3c..00000000000 Binary files a/Utilities/cmbzip2/sample1.rb2 and /dev/null differ diff --git a/Utilities/cmbzip2/sample1.ref b/Utilities/cmbzip2/sample1.ref deleted file mode 100644 index dc869ee6dc8..00000000000 Binary files a/Utilities/cmbzip2/sample1.ref and /dev/null differ diff --git a/Utilities/cmbzip2/sample1.tst b/Utilities/cmbzip2/sample1.tst deleted file mode 100644 index dc869ee6dc8..00000000000 Binary files a/Utilities/cmbzip2/sample1.tst and /dev/null differ diff --git a/Utilities/cmbzip2/sample2.rb2 b/Utilities/cmbzip2/sample2.rb2 deleted file mode 100644 index 8e54297c946..00000000000 Binary files a/Utilities/cmbzip2/sample2.rb2 and /dev/null differ diff --git a/Utilities/cmbzip2/sample2.ref b/Utilities/cmbzip2/sample2.ref deleted file mode 100644 index 40e5b58f2ef..00000000000 Binary files a/Utilities/cmbzip2/sample2.ref and /dev/null differ diff --git a/Utilities/cmbzip2/sample2.tst b/Utilities/cmbzip2/sample2.tst deleted file mode 100644 index 40e5b58f2ef..00000000000 Binary files a/Utilities/cmbzip2/sample2.tst and /dev/null differ diff --git a/Utilities/cmbzip2/sample3.rb2 b/Utilities/cmbzip2/sample3.rb2 deleted file mode 100644 index 1c9b08c476d..00000000000 Binary files a/Utilities/cmbzip2/sample3.rb2 and /dev/null differ diff --git a/Utilities/cmbzip2/sample3.ref b/Utilities/cmbzip2/sample3.ref deleted file mode 100644 index 775a2f68e21..00000000000 --- a/Utilities/cmbzip2/sample3.ref +++ /dev/null @@ -1,30007 +0,0 @@ -This file is exceedingly boring. If you find yourself -reading it, please (1) take it from me that you can safely -guess what the rest of the file says, and (2) seek professional -help. - -ps. there are no further sarcastic remarks in this file. - -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh diff --git a/Utilities/cmbzip2/sample3.tst b/Utilities/cmbzip2/sample3.tst deleted file mode 100644 index 775a2f68e21..00000000000 --- a/Utilities/cmbzip2/sample3.tst +++ /dev/null @@ -1,30007 +0,0 @@ -This file is exceedingly boring. If you find yourself -reading it, please (1) take it from me that you can safely -guess what the rest of the file says, and (2) seek professional -help. - -ps. there are no further sarcastic remarks in this file. - -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh -ugh diff --git a/Utilities/cmbzip2/spewG.c b/Utilities/cmbzip2/spewG.c index 7bd12841d0c..65d24c89cbc 100644 --- a/Utilities/cmbzip2/spewG.c +++ b/Utilities/cmbzip2/spewG.c @@ -13,15 +13,15 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. This program is released under the terms of the license contained in the file LICENSE. - ------------------------------------------------------------------ */ + ------------------------------------------------------------------ */ #define _FILE_OFFSET_BITS 64 diff --git a/Utilities/cmbzip2/unzcrash.c b/Utilities/cmbzip2/unzcrash.c index a1b75463adc..c68f93c56cb 100644 --- a/Utilities/cmbzip2/unzcrash.c +++ b/Utilities/cmbzip2/unzcrash.c @@ -17,8 +17,8 @@ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. diff --git a/Utilities/cmbzip2/words0 b/Utilities/cmbzip2/words0 deleted file mode 100644 index fbf442ad6bf..00000000000 --- a/Utilities/cmbzip2/words0 +++ /dev/null @@ -1,9 +0,0 @@ - -If compilation produces errors, or a large number of warnings, -please read README.COMPILATION.PROBLEMS -- you might be able to -adjust the flags in this Makefile to improve matters. - -Also in README.COMPILATION.PROBLEMS are some hints that may help -if your build produces an executable which is unable to correctly -handle so-called 'large files' -- files of size 2GB or more. - diff --git a/Utilities/cmbzip2/words1 b/Utilities/cmbzip2/words1 deleted file mode 100644 index 2e83de9f083..00000000000 --- a/Utilities/cmbzip2/words1 +++ /dev/null @@ -1,4 +0,0 @@ - -Doing 6 tests (3 compress, 3 uncompress) ... -If there's a problem, things might stop at this point. - diff --git a/Utilities/cmbzip2/words2 b/Utilities/cmbzip2/words2 deleted file mode 100644 index caddcf42266..00000000000 --- a/Utilities/cmbzip2/words2 +++ /dev/null @@ -1,5 +0,0 @@ - -Checking test results. If any of the four "cmp"s which follow -report any differences, something is wrong. If you can't easily -figure out what, please let me know (jseward@bzip.org). - diff --git a/Utilities/cmbzip2/words3 b/Utilities/cmbzip2/words3 deleted file mode 100644 index 6972669906d..00000000000 --- a/Utilities/cmbzip2/words3 +++ /dev/null @@ -1,30 +0,0 @@ - -If you got this far and the 'cmp's didn't complain, it looks -like you're in business. - -To install in /usr/local/bin, /usr/local/lib, /usr/local/man and -/usr/local/include, type - - make install - -To install somewhere else, eg, /xxx/yyy/{bin,lib,man,include}, type - - make install PREFIX=/xxx/yyy - -If you are (justifiably) paranoid and want to see what 'make install' -is going to do, you can first do - - make -n install or - make -n install PREFIX=/xxx/yyy respectively. - -The -n instructs make to show the commands it would execute, but -not actually execute them. - -Instructions for use are in the preformatted manual page, in the file -bzip2.txt. For more detailed documentation, read the full manual. -It is available in Postscript form (manual.ps), PDF form (manual.pdf), -and HTML form (manual.html). - -You can also do "bzip2 --help" to see some helpful information. -"bzip2 -L" displays the software license. - diff --git a/Utilities/cmbzip2/xmlproc.sh b/Utilities/cmbzip2/xmlproc.sh deleted file mode 100755 index 53841773d44..00000000000 --- a/Utilities/cmbzip2/xmlproc.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/bash -# see the README file for usage etc. -# -# ------------------------------------------------------------------ -# This file is part of bzip2/libbzip2, a program and library for -# lossless, block-sorting data compression. -# -# bzip2/libbzip2 version 1.0.5 of 10 December 2007 -# Copyright (C) 1996-2007 Julian Seward -# -# Please read the WARNING, DISCLAIMER and PATENTS sections in the -# README file. -# -# This program is released under the terms of the license contained -# in the file LICENSE. -# ---------------------------------------------------------------- - - -usage() { - echo ''; - echo 'Usage: xmlproc.sh -[option] '; - echo 'Specify a target from:'; - echo '-v verify xml file conforms to dtd'; - echo '-html output in html format (single file)'; - echo '-ps output in postscript format'; - echo '-pdf output in pdf format'; - exit; -} - -if test $# -ne 2; then - usage -fi -# assign the variable for the output type -action=$1; shift -# assign the output filename -xmlfile=$1; shift -# and check user input it correct -if !(test -f $xmlfile); then - echo "No such file: $xmlfile"; - exit; -fi -# some other stuff we will use -OUT=output -xsl_fo=bz-fo.xsl -xsl_html=bz-html.xsl - -basename=$xmlfile -basename=${basename//'.xml'/''} - -fofile="${basename}.fo" -htmlfile="${basename}.html" -pdffile="${basename}.pdf" -psfile="${basename}.ps" -xmlfmtfile="${basename}.fmt" - -# first process the xmlfile with CDATA tags -./format.pl $xmlfile $xmlfmtfile -# so the shell knows where the catalogs live -export XML_CATALOG_FILES=/etc/xml/catalog - -# post-processing tidy up -cleanup() { - echo "Cleaning up: $@" - while [ $# != 0 ] - do - arg=$1; shift; - echo " deleting $arg"; - rm $arg - done -} - -case $action in - -v) - flags='--noout --xinclude --noblanks --postvalid' - dtd='--dtdvalid http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd' - xmllint $flags $dtd $xmlfmtfile 2> $OUT - egrep 'error' $OUT - rm $OUT - ;; - - -html) - echo "Creating $htmlfile ..." - xsltproc --nonet --xinclude -o $htmlfile $xsl_html $xmlfmtfile - cleanup $xmlfmtfile - ;; - - -pdf) - echo "Creating $pdffile ..." - xsltproc --nonet --xinclude -o $fofile $xsl_fo $xmlfmtfile - pdfxmltex $fofile >$OUT $OUT $OUT $OUT $OUT $OUT $OUT $OUT $OUT , 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.haxx.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. +# +########################################################################### @CMAKE_CONFIGURABLE_FILE_CONTENT@ diff --git a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake index 60ee8e68238..aaac9feadfd 100644 --- a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake +++ b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### include(CheckCSourceCompiles) option(CURL_HIDDEN_SYMBOLS "Set to ON to hide libcurl internal symbols (=hide all symbols that aren't officially external)." ON) @@ -11,13 +32,7 @@ if(CURL_HIDDEN_SYMBOLS) set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))") set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") elseif(CMAKE_COMPILER_IS_GNUCC) - if(NOT CMAKE_VERSION VERSION_LESS 2.8.10) - set(GCC_VERSION ${CMAKE_C_COMPILER_VERSION}) - else() - execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion - OUTPUT_VARIABLE GCC_VERSION) - endif() - if(NOT GCC_VERSION VERSION_LESS 3.4) + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.4) # note: this is considered buggy prior to 4.0 but the autotools don't care, so let's ignore that fact set(SUPPORTS_SYMBOL_HIDING TRUE) set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))") @@ -29,7 +44,7 @@ if(CURL_HIDDEN_SYMBOLS) set(_CFLAG_SYMBOLS_HIDE "-xldscope=hidden") elseif(CMAKE_C_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.0) # note: this should probably just check for version 9.1.045 but I'm not 100% sure - # so let's to it the same way autotools do. + # so let's do it the same way autotools do. set(SUPPORTS_SYMBOL_HIDING TRUE) set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))") set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c index 07b516b4dae..3ef35f02535 100644 --- a/Utilities/cmcurl/CMake/CurlTests.c +++ b/Utilities/cmcurl/CMake/CurlTests.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -125,6 +125,7 @@ int main(void) #if defined(HAVE_GETHOSTBYADDR_R_5) || \ defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) rc = gethostbyaddr_r(address, length, type, &h, &hdata); + (void)rc; #elif defined(HAVE_GETHOSTBYADDR_R_7) || \ defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) hp = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &h_errnop); @@ -132,6 +133,7 @@ int main(void) #elif defined(HAVE_GETHOSTBYADDR_R_8) || \ defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) rc = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &hp, &h_errnop); + (void)rc; #endif #if defined(HAVE_GETHOSTBYNAME_R_3) || \ diff --git a/Utilities/cmcurl/CMake/FindBearSSL.cmake b/Utilities/cmcurl/CMake/FindBearSSL.cmake new file mode 100644 index 00000000000..a8f72c92d28 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindBearSSL.cmake @@ -0,0 +1,30 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### +find_path(BEARSSL_INCLUDE_DIRS bearssl.h) + +find_library(BEARSSL_LIBRARY bearssl) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(BEARSSL DEFAULT_MSG + BEARSSL_INCLUDE_DIRS BEARSSL_LIBRARY) + +mark_as_advanced(BEARSSL_INCLUDE_DIRS BEARSSL_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindBrotli.cmake b/Utilities/cmcurl/CMake/FindBrotli.cmake index 351b8f757b8..c43172b6407 100644 --- a/Utilities/cmcurl/CMake/FindBrotli.cmake +++ b/Utilities/cmcurl/CMake/FindBrotli.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### include(FindPackageHandleStandardArgs) find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") diff --git a/Utilities/cmcurl/CMake/FindCARES.cmake b/Utilities/cmcurl/CMake/FindCARES.cmake index 723044a6442..9160ae5fd66 100644 --- a/Utilities/cmcurl/CMake/FindCARES.cmake +++ b/Utilities/cmcurl/CMake/FindCARES.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### # - Find c-ares # Find the c-ares includes and library # This module defines @@ -7,34 +28,16 @@ # also defined, but not for general use are # CARES_LIBRARY, where to find the c-ares library. -find_path(CARES_INCLUDE_DIR ares.h - /usr/local/include - /usr/include - ) +find_path(CARES_INCLUDE_DIR ares.h) set(CARES_NAMES ${CARES_NAMES} cares) find_library(CARES_LIBRARY NAMES ${CARES_NAMES} - PATHS /usr/lib /usr/local/lib ) -if(CARES_LIBRARY AND CARES_INCLUDE_DIR) - set(CARES_LIBRARIES ${CARES_LIBRARY}) - set(CARES_FOUND "YES") -else() - set(CARES_FOUND "NO") -endif() - - -if(CARES_FOUND) - if(NOT CARES_FIND_QUIETLY) - message(STATUS "Found c-ares: ${CARES_LIBRARIES}") - endif() -else() - if(CARES_FIND_REQUIRED) - message(FATAL_ERROR "Could not find c-ares library") - endif() -endif() +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CARES + REQUIRED_VARS CARES_LIBRARY CARES_INCLUDE_DIR) mark_as_advanced( CARES_LIBRARY diff --git a/Utilities/cmcurl/CMake/FindGSS.cmake b/Utilities/cmcurl/CMake/FindGSS.cmake index 8a28f2fb617..02111a20fe0 100644 --- a/Utilities/cmcurl/CMake/FindGSS.cmake +++ b/Utilities/cmcurl/CMake/FindGSS.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### # - Try to find the GSS Kerberos library # Once done this will define # @@ -62,6 +83,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac COMMAND ${_GSS_CONFIGURE_SCRIPT} "--cflags" "gssapi" OUTPUT_VARIABLE _GSS_CFLAGS RESULT_VARIABLE _GSS_CONFIGURE_FAILED + OUTPUT_STRIP_TRAILING_WHITESPACE ) message(STATUS "CFLAGS: ${_GSS_CFLAGS}") if(NOT _GSS_CONFIGURE_FAILED) # 0 means success @@ -84,6 +106,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac COMMAND ${_GSS_CONFIGURE_SCRIPT} "--libs" "gssapi" OUTPUT_VARIABLE _GSS_LIB_FLAGS RESULT_VARIABLE _GSS_CONFIGURE_FAILED + OUTPUT_STRIP_TRAILING_WHITESPACE ) message(STATUS "LDFLAGS: ${_GSS_LIB_FLAGS}") @@ -110,6 +133,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac COMMAND ${_GSS_CONFIGURE_SCRIPT} "--version" OUTPUT_VARIABLE _GSS_VERSION RESULT_VARIABLE _GSS_CONFIGURE_FAILED + OUTPUT_STRIP_TRAILING_WHITESPACE ) # older versions may not have the "--version" parameter. In this case we just don't care. @@ -121,6 +145,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac COMMAND ${_GSS_CONFIGURE_SCRIPT} "--vendor" OUTPUT_VARIABLE _GSS_VENDOR RESULT_VARIABLE _GSS_CONFIGURE_FAILED + OUTPUT_STRIP_TRAILING_WHITESPACE ) # older versions may not have the "--vendor" parameter. In this case we just don't care. @@ -134,7 +159,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac endif() endif() - else() # either there is no config script or we are on platform that doesn't provide one (Windows?) + else() # either there is no config script or we are on a platform that doesn't provide one (Windows?) find_path(_GSS_INCLUDE_DIR NAMES @@ -164,7 +189,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac set(CMAKE_REQUIRED_DEFINITIONS "") endif() else() - # I'm not convienced if this is the right way but this is what autotools do at the moment + # I'm not convinced if this is the right way but this is what autotools do at the moment find_path(_GSS_INCLUDE_DIR NAMES "gssapi.h" diff --git a/Utilities/cmcurl/CMake/FindLibSSH2.cmake b/Utilities/cmcurl/CMake/FindLibSSH2.cmake index 84822dba74d..4cdf3e31032 100644 --- a/Utilities/cmcurl/CMake/FindLibSSH2.cmake +++ b/Utilities/cmcurl/CMake/FindLibSSH2.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### # - Try to find the libssh2 library # Once done this will define # @@ -5,31 +26,18 @@ # LIBSSH2_INCLUDE_DIR - the libssh2 include directory # LIBSSH2_LIBRARY - the libssh2 library name -if(LIBSSH2_INCLUDE_DIR AND LIBSSH2_LIBRARY) - set(LibSSH2_FIND_QUIETLY TRUE) -endif() - -find_path(LIBSSH2_INCLUDE_DIR libssh2.h -) +find_path(LIBSSH2_INCLUDE_DIR libssh2.h) -find_library(LIBSSH2_LIBRARY NAMES ssh2 -) +find_library(LIBSSH2_LIBRARY NAMES ssh2 libssh2) if(LIBSSH2_INCLUDE_DIR) - file(STRINGS "${LIBSSH2_INCLUDE_DIR}/libssh2.h" libssh2_version_str REGEX "^#define[\t ]+LIBSSH2_VERSION_NUM[\t ]+0x[0-9][0-9][0-9][0-9][0-9][0-9].*") - - string(REGEX REPLACE "^.*LIBSSH2_VERSION_NUM[\t ]+0x([0-9][0-9]).*$" "\\1" LIBSSH2_VERSION_MAJOR "${libssh2_version_str}") - string(REGEX REPLACE "^.*LIBSSH2_VERSION_NUM[\t ]+0x[0-9][0-9]([0-9][0-9]).*$" "\\1" LIBSSH2_VERSION_MINOR "${libssh2_version_str}") - string(REGEX REPLACE "^.*LIBSSH2_VERSION_NUM[\t ]+0x[0-9][0-9][0-9][0-9]([0-9][0-9]).*$" "\\1" LIBSSH2_VERSION_PATCH "${libssh2_version_str}") - - string(REGEX REPLACE "^0(.+)" "\\1" LIBSSH2_VERSION_MAJOR "${LIBSSH2_VERSION_MAJOR}") - string(REGEX REPLACE "^0(.+)" "\\1" LIBSSH2_VERSION_MINOR "${LIBSSH2_VERSION_MINOR}") - string(REGEX REPLACE "^0(.+)" "\\1" LIBSSH2_VERSION_PATCH "${LIBSSH2_VERSION_PATCH}") - - set(LIBSSH2_VERSION "${LIBSSH2_VERSION_MAJOR}.${LIBSSH2_VERSION_MINOR}.${LIBSSH2_VERSION_PATCH}") + file(STRINGS "${LIBSSH2_INCLUDE_DIR}/libssh2.h" libssh2_version_str REGEX "^#define[\t ]+LIBSSH2_VERSION[\t ]+\"(.*)\"") + string(REGEX REPLACE "^.*\"([^\"]+)\"" "\\1" LIBSSH2_VERSION "${libssh2_version_str}") endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LibSSH2 DEFAULT_MSG LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY ) +find_package_handle_standard_args(LibSSH2 + REQUIRED_VARS LIBSSH2_LIBRARY LIBSSH2_INCLUDE_DIR + VERSION_VAR LIBSSH2_VERSION) -mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY LIBSSH2_VERSION_MAJOR LIBSSH2_VERSION_MINOR LIBSSH2_VERSION_PATCH LIBSSH2_VERSION) +mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake index a9163958921..2ebe721a64f 100644 --- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake +++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h) find_library(MBEDTLS_LIBRARY mbedtls) diff --git a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake index 348b9612dfa..e1eba05327a 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### include(FindPackageHandleStandardArgs) find_path(NGHTTP2_INCLUDE_DIR "nghttp2/nghttp2.h") @@ -10,9 +31,9 @@ find_package_handle_standard_args(NGHTTP2 REQUIRED_VARS NGHTTP2_LIBRARY NGHTTP2_INCLUDE_DIR - FAIL_MESSAGE - "Could NOT find NGHTTP2" ) set(NGHTTP2_INCLUDE_DIRS ${NGHTTP2_INCLUDE_DIR}) set(NGHTTP2_LIBRARIES ${NGHTTP2_LIBRARY}) + +mark_as_advanced(NGHTTP2_INCLUDE_DIRS NGHTTP2_LIBRARIES) diff --git a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake new file mode 100644 index 00000000000..73ce9e18839 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake @@ -0,0 +1,76 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### + +#[=======================================================================[.rst: +FindNGHTTP3 +---------- + +Find the nghttp3 library + +Result Variables +^^^^^^^^^^^^^^^^ + +``NGHTTP3_FOUND`` + System has nghttp3 +``NGHTTP3_INCLUDE_DIRS`` + The nghttp3 include directories. +``NGHTTP3_LIBRARIES`` + The libraries needed to use nghttp3 +``NGHTTP3_VERSION`` + version of nghttp3. +#]=======================================================================] + +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_NGHTTP3 libnghttp3) +endif() + +find_path(NGHTTP3_INCLUDE_DIR nghttp3/nghttp3.h + HINTS + ${PC_NGHTTP3_INCLUDEDIR} + ${PC_NGHTTP3_INCLUDE_DIRS} +) + +find_library(NGHTTP3_LIBRARY NAMES nghttp3 + HINTS + ${PC_NGHTTP3_LIBDIR} + ${PC_NGHTTP3_LIBRARY_DIRS} +) + +if(PC_NGHTTP3_VERSION) + set(NGHTTP3_VERSION ${PC_NGHTTP3_VERSION}) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(NGHTTP3 + REQUIRED_VARS + NGHTTP3_LIBRARY + NGHTTP3_INCLUDE_DIR + VERSION_VAR NGHTTP3_VERSION +) + +if(NGHTTP3_FOUND) + set(NGHTTP3_LIBRARIES ${NGHTTP3_LIBRARY}) + set(NGHTTP3_INCLUDE_DIRS ${NGHTTP3_INCLUDE_DIR}) +endif() + +mark_as_advanced(NGHTTP3_INCLUDE_DIRS NGHTTP3_LIBRARIES) diff --git a/Utilities/cmcurl/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake new file mode 100644 index 00000000000..a1ed8cd4be5 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake @@ -0,0 +1,113 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### + +#[=======================================================================[.rst: +FindNGTCP2 +---------- + +Find the ngtcp2 library + +This module accepts optional COMPONENTS to control the crypto library (these are +mutually exclusive):: + + OpenSSL: Use libngtcp2_crypto_openssl + GnuTLS: Use libngtcp2_crypto_gnutls + +Result Variables +^^^^^^^^^^^^^^^^ + +``NGTCP2_FOUND`` + System has ngtcp2 +``NGTCP2_INCLUDE_DIRS`` + The ngtcp2 include directories. +``NGTCP2_LIBRARIES`` + The libraries needed to use ngtcp2 +``NGTCP2_VERSION`` + version of ngtcp2. +#]=======================================================================] + +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_NGTCP2 libngtcp2) +endif() + +find_path(NGTCP2_INCLUDE_DIR ngtcp2/ngtcp2.h + HINTS + ${PC_NGTCP2_INCLUDEDIR} + ${PC_NGTCP2_INCLUDE_DIRS} +) + +find_library(NGTCP2_LIBRARY NAMES ngtcp2 + HINTS + ${PC_NGTCP2_LIBDIR} + ${PC_NGTCP2_LIBRARY_DIRS} +) + +if(PC_NGTCP2_VERSION) + set(NGTCP2_VERSION ${PC_NGTCP2_VERSION}) +endif() + +if(NGTCP2_FIND_COMPONENTS) + set(NGTCP2_CRYPTO_BACKEND "") + foreach(component IN LISTS NGTCP2_FIND_COMPONENTS) + if(component MATCHES "^(OpenSSL|GnuTLS)") + if(NGTCP2_CRYPTO_BACKEND) + message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected") + endif() + set(NGTCP2_CRYPTO_BACKEND ${component}) + endif() + endforeach() + + if(NGTCP2_CRYPTO_BACKEND) + string(TOLOWER "ngtcp2_crypto_${NGTCP2_CRYPTO_BACKEND}" _crypto_library) + if(UNIX) + pkg_search_module(PC_${_crypto_library} lib${_crypto_library}) + endif() + find_library(${_crypto_library}_LIBRARY + NAMES + ${_crypto_library} + HINTS + ${PC_${_crypto_library}_LIBDIR} + ${PC_${_crypto_library}_LIBRARY_DIRS} + ) + if(${_crypto_library}_LIBRARY) + set(NGTCP2_${NGTCP2_CRYPTO_BACKEND}_FOUND TRUE) + set(NGTCP2_CRYPTO_LIBRARY ${${_crypto_library}_LIBRARY}) + endif() + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(NGTCP2 + REQUIRED_VARS + NGTCP2_LIBRARY + NGTCP2_INCLUDE_DIR + VERSION_VAR NGTCP2_VERSION + HANDLE_COMPONENTS +) + +if(NGTCP2_FOUND) + set(NGTCP2_LIBRARIES ${NGTCP2_LIBRARY} ${NGTCP2_CRYPTO_LIBRARY}) + set(NGTCP2_INCLUDE_DIRS ${NGTCP2_INCLUDE_DIR}) +endif() + +mark_as_advanced(NGTCP2_INCLUDE_DIRS NGTCP2_LIBRARIES) diff --git a/Utilities/cmcurl/CMake/FindNSS.cmake b/Utilities/cmcurl/CMake/FindNSS.cmake new file mode 100644 index 00000000000..5fdb2b776e0 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindNSS.cmake @@ -0,0 +1,38 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_NSS nss) +endif() +if(NOT PC_NSS_FOUND) + return() +endif() + +set(NSS_LIBRARIES ${PC_NSS_LINK_LIBRARIES}) +set(NSS_INCLUDE_DIRS ${PC_NSS_INCLUDE_DIRS}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(NSS + REQUIRED_VARS NSS_LIBRARIES NSS_INCLUDE_DIRS + VERSION_VAR PC_NSS_VERSION) + +mark_as_advanced(NSS_INCLUDE_DIRS NSS_LIBRARIES) diff --git a/Utilities/cmcurl/CMake/FindQUICHE.cmake b/Utilities/cmcurl/CMake/FindQUICHE.cmake new file mode 100644 index 00000000000..01d17582bef --- /dev/null +++ b/Utilities/cmcurl/CMake/FindQUICHE.cmake @@ -0,0 +1,68 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### + +#[=======================================================================[.rst: +FindQUICHE +---------- + +Find the quiche library + +Result Variables +^^^^^^^^^^^^^^^^ + +``QUICHE_FOUND`` + System has quiche +``QUICHE_INCLUDE_DIRS`` + The quiche include directories. +``QUICHE_LIBRARIES`` + The libraries needed to use quiche +#]=======================================================================] +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_QUICHE quiche) +endif() + +find_path(QUICHE_INCLUDE_DIR quiche.h + HINTS + ${PC_QUICHE_INCLUDEDIR} + ${PC_QUICHE_INCLUDE_DIRS} +) + +find_library(QUICHE_LIBRARY NAMES quiche + HINTS + ${PC_QUICHE_LIBDIR} + ${PC_QUICHE_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QUICHE + REQUIRED_VARS + QUICHE_LIBRARY + QUICHE_INCLUDE_DIR +) + +if(QUICHE_FOUND) + set(QUICHE_LIBRARIES ${QUICHE_LIBRARY}) + set(QUICHE_INCLUDE_DIRS ${QUICHE_INCLUDE_DIR}) +endif() + +mark_as_advanced(QUICHE_INCLUDE_DIRS QUICHE_LIBRARIES) diff --git a/Utilities/cmcurl/CMake/FindWolfSSL.cmake b/Utilities/cmcurl/CMake/FindWolfSSL.cmake new file mode 100644 index 00000000000..54df1a86c03 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindWolfSSL.cmake @@ -0,0 +1,34 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### +find_path(WolfSSL_INCLUDE_DIR NAMES wolfssl/ssl.h) +find_library(WolfSSL_LIBRARY NAMES wolfssl) +mark_as_advanced(WolfSSL_INCLUDE_DIR WolfSSL_LIBRARY) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WolfSSL + REQUIRED_VARS WolfSSL_INCLUDE_DIR WolfSSL_LIBRARY + ) + +if(WolfSSL_FOUND) + set(WolfSSL_INCLUDE_DIRS ${WolfSSL_INCLUDE_DIR}) + set(WolfSSL_LIBRARIES ${WolfSSL_LIBRARY}) +endif() diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake index 7f71345156c..65a41e484a9 100644 --- a/Utilities/cmcurl/CMake/Macros.cmake +++ b/Utilities/cmcurl/CMake/Macros.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### #File defines convenience macros for available feature testing # This macro checks if the symbol exists in the library and if it diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake index c1c9aa32ab5..8c9a491946e 100644 --- a/Utilities/cmcurl/CMake/OtherTests.cmake +++ b/Utilities/cmcurl/CMake/OtherTests.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### include(CheckCSourceCompiles) # The begin of the sources (macros and includes) set(_source_epilogue "#undef inline") @@ -26,13 +47,20 @@ endif() set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +if(1) # CMake hard-codes these + set(RECV_TYPE_ARG1 "curl_socket_t") + set(RECV_TYPE_ARG2 "char *") + set(RECV_TYPE_ARG3 "size_t") + set(RECV_TYPE_ARG4 "int") + set(RECV_TYPE_RETV "ssize_t") +else() check_c_source_compiles("${_source_epilogue} int main(void) { recv(0, 0, 0, 0); return 0; }" curl_cv_recv) if(curl_cv_recv) - if(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown") + if(NOT DEFINED curl_cv_func_recv_args OR curl_cv_func_recv_args STREQUAL "unknown") foreach(recv_retv "int" "ssize_t" ) foreach(recv_arg1 "SOCKET" "int" ) foreach(recv_arg2 "char *" "void *" ) @@ -42,6 +70,9 @@ if(curl_cv_recv) unset(curl_cv_func_recv_test CACHE) check_c_source_compiles(" ${_source_epilogue} + #ifdef WINSOCK_API_LINKAGE + WINSOCK_API_LINKAGE + #endif extern ${recv_retv} ${signature_call_conv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4}); int main(void) { @@ -81,15 +112,24 @@ if(curl_cv_recv) string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" RECV_TYPE_RETV "${curl_cv_func_recv_args}") endif() - if("${curl_cv_func_recv_args}" STREQUAL "unknown") + if(curl_cv_func_recv_args STREQUAL "unknown") message(FATAL_ERROR "Cannot find proper types to use for recv args") endif() else() message(FATAL_ERROR "Unable to link function recv") endif() set(curl_cv_func_recv_args "${curl_cv_func_recv_args}" CACHE INTERNAL "Arguments for recv") +endif() set(HAVE_RECV 1) +if(1) # CMake hard-codes these + set(SEND_QUAL_ARG2 " ") + set(SEND_TYPE_ARG1 "curl_socket_t") + set(SEND_TYPE_ARG2 "char *") + set(SEND_TYPE_ARG3 "size_t") + set(SEND_TYPE_ARG4 "int") + set(SEND_TYPE_RETV "ssize_t") +else() check_c_source_compiles("${_source_epilogue} int main(void) { send(0, 0, 0, 0); @@ -106,6 +146,9 @@ if(curl_cv_send) unset(curl_cv_func_send_test CACHE) check_c_source_compiles(" ${_source_epilogue} + #ifdef WINSOCK_API_LINKAGE + WINSOCK_API_LINKAGE + #endif extern ${send_retv} ${signature_call_conv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4}); int main(void) { @@ -156,6 +199,7 @@ else() message(FATAL_ERROR "Unable to link function send") endif() set(curl_cv_func_send_args "${curl_cv_func_send_args}" CACHE INTERNAL "Arguments for send") +endif() set(HAVE_SEND 1) check_c_source_compiles("${_source_epilogue} diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake index 2dbe1bbbd01..7c020bd499f 100644 --- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake +++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### if(NOT UNIX) if(WIN32) set(HAVE_LIBDL 0) @@ -7,7 +28,6 @@ if(NOT UNIX) set(HAVE_LIBNSL 0) set(HAVE_GETHOSTNAME 1) set(HAVE_LIBZ 0) - set(HAVE_LIBCRYPTO 0) set(HAVE_DLOPEN 0) diff --git a/Utilities/cmcurl/CMake/Utilities.cmake b/Utilities/cmcurl/CMake/Utilities.cmake index 5cb1d449754..59b17d07440 100644 --- a/Utilities/cmcurl/CMake/Utilities.cmake +++ b/Utilities/cmcurl/CMake/Utilities.cmake @@ -1,13 +1,33 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### # File containing various utilities # Returns a list of arguments that evaluate to true function(count_true output_count_var) - set(lst) + set(lst_len 0) foreach(option_var IN LISTS ARGN) if(${option_var}) - list(APPEND lst ${option_var}) + math(EXPR lst_len "${lst_len} + 1") endif() endforeach() - list(LENGTH lst lst_len) set(${output_count_var} ${lst_len} PARENT_SCOPE) endfunction() diff --git a/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in index 5178fd8281d..74e56a29c27 100644 --- a/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in +++ b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif() diff --git a/Utilities/cmcurl/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in index 1294e173a00..ae8cc30f1a6 100644 --- a/Utilities/cmcurl/CMake/curl-config.cmake.in +++ b/Utilities/cmcurl/CMake/curl-config.cmake.in @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### @PACKAGE_INIT@ include(CMakeFindDependencyMacro) diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index bc8a7dc37b8..d327f550b52 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -5,6 +5,7 @@ set(BUILD_RELEASE_DEBUG_DIRS OFF CACHE INTERNAL "No curl release/debug dirs") set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Build shared libraries") set(CMAKE_USE_GSSAPI OFF CACHE INTERNAL "Disable curl gssapi") set(CMAKE_USE_LIBSSH2 OFF CACHE INTERNAL "Disable curl libssh2") +set(CMAKE_USE_LIBSSH OFF) set(CMAKE_USE_OPENLDAP OFF CACHE INTERNAL "No curl OpenLDAP") set(CURL_DISABLE_COOKIES OFF CACHE INTERNAL "Do not disable curl cookie support") set(CURL_DISABLE_CRYPTO_AUTH OFF CACHE INTERNAL "Do not disable curl crypto auth") @@ -23,18 +24,26 @@ set(CURL_DISABLE_SMTP ON CACHE INTERNAL "Disable curl smtp protocol?") set(CURL_DISABLE_TELNET ON CACHE INTERNAL "Disable curl telnet protocol?") set(CURL_DISABLE_TFTP ON CACHE INTERNAL "Disable curl tftp protocol?") set(CURL_DISABLE_VERBOSE_STRINGS OFF CACHE INTERNAL "Do not disable curl verbosity") +set(CURL_ENABLE_MQTT OFF) 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_WERROR OFF CACHE INTERNAL "Turn compiler warnings into errors") set(DISABLED_THREADSAFE OFF CACHE INTERNAL "Curl can use thread-safe functions") set(ENABLE_ARES OFF CACHE INTERNAL "No curl c-ares support") +set(ENABLE_ALT_SVC OFF) set(ENABLE_CURLDEBUG OFF CACHE INTERNAL "No curl TrackMemory features") set(ENABLE_DEBUG OFF CACHE INTERNAL "No curl debug features") +set(ENABLE_INET_PTON OFF CACHE INTERNAL "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc.") set(ENABLE_IPV6 ON CACHE INTERNAL "Enable curl IPv6 support detection") set(ENABLE_MANUAL OFF CACHE INTERNAL "No curl built-in manual") set(ENABLE_THREADED_RESOLVER OFF CACHE INTERNAL "No curl POSIX threaded DNS lookup") set(ENABLE_UNIX_SOCKETS OFF CACHE INTERNAL "No curl Unix domain sockets support") set(HTTP_ONLY OFF CACHE INTERNAL "Curl is not http-only") set(PICKY_COMPILER OFF CACHE INTERNAL "Enable picky compiler options") +set(USE_NGHTTP2 ON) +set(USE_NGTCP2 OFF) +set(USE_QUICHE OFF) set(USE_WIN32_LDAP OFF CACHE INTERNAL "No curl Windows LDAP") if(CMAKE_USE_OPENSSL) elseif(WIN32) @@ -60,6 +69,9 @@ elseif(APPLE) unset(CMAKE_USE_DARWINSSL CACHE) endif() set(CMAKE_USE_MBEDTLS OFF CACHE INTERNAL "Enable mbedTLS for SSL/TLS") +set(CMAKE_USE_BEARSSL OFF CACHE INTERNAL "Enable BearSSL for SSL/TLS") +set(CMAKE_USE_NSS OFF CACHE INTERNAL "Enable NSS for SSL/TLS") +set(CMAKE_USE_WOLFSSL OFF) # Windows Vista and above have inet_pton, but this will link on # older versions and then the executable will fail to launch at @@ -91,7 +103,7 @@ endif() # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -112,7 +124,6 @@ endif() # The output .so file lacks the soname number which we currently have within the lib/Makefile.am file # Add full (4 or 5 libs) SSL support # Add INSTALL target (EXTRA_DIST variables in Makefile.am may be moved to Makefile.inc so that CMake/CPack is aware of what's to include). -# Add CTests(?) # Check on all possible platforms # Test with as many configurations possible (With or without any option) # Create scripts that help keeping the CMake build system up to date (to reduce maintenance). According to Tetetest: @@ -136,7 +147,7 @@ if(0) # This code not needed for building within CMake. message(WARNING "the curl cmake build system is poorly maintained. Be aware") endif() -file(READ ${CURL_SOURCE_DIR}/include/curl/curlver.h CURL_VERSION_H_CONTENTS) +file(STRINGS ${CURL_SOURCE_DIR}/include/curl/curlver.h CURL_VERSION_H_CONTENTS REGEX "#define LIBCURL_VERSION( |_NUM )") string(REGEX MATCH "#define LIBCURL_VERSION \"[^\"]*" CURL_VERSION ${CURL_VERSION_H_CONTENTS}) string(REGEX REPLACE "[^\"]+\"" "" CURL_VERSION ${CURL_VERSION}) @@ -168,7 +179,20 @@ option(ENABLE_ARES "Set to ON to enable c-ares support" OFF) if(WIN32) option(CURL_STATIC_CRT "Set to ON to build libcurl with static CRT on Windows (/MT)." OFF) option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON) + if(0) # This code not needed for building within CMake. + set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string") + if(CURL_TARGET_WINDOWS_VERSION) + add_definitions(-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}) + elseif(ENABLE_INET_PTON) + # _WIN32_WINNT_VISTA (0x0600) + add_definitions(-D_WIN32_WINNT=0x0600) + else() + # _WIN32_WINNT_WINXP (0x0501) + add_definitions(-D_WIN32_WINNT=0x0501) + endif() + endif() endif() +option(CURL_LTO "Turn on compiler Link Time Optimizations" OFF) if(0) # This code not needed for building within CMake. cmake_dependent_option(ENABLE_THREADED_RESOLVER "Set to ON to enable threaded DNS lookup" @@ -184,8 +208,9 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) foreach(_CCOPT -pedantic -Wall -W -Wpointer-arith -Wwrite-strings -Wunused -Wshadow -Winline -Wnested-externs -Wmissing-declarations -Wmissing-prototypes -Wno-long-long -Wfloat-equal -Wno-multichar -Wsign-compare -Wundef -Wno-format-nonliteral -Wendif-labels -Wstrict-prototypes -Wdeclaration-after-statement -Wstrict-aliasing=3 -Wcast-align -Wtype-limits -Wold-style-declaration -Wmissing-parameter-type -Wempty-body -Wclobbered -Wignored-qualifiers -Wconversion -Wno-sign-conversion -Wvla -Wdouble-promotion -Wno-system-headers -Wno-pedantic-ms-format) # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new # test result in. - check_c_compiler_flag(${_CCOPT} OPT${_CCOPT}) - if(OPT${_CCOPT}) + string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname) + check_c_compiler_flag(${_CCOPT} ${_optvarname}) + if(${_optvarname}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_CCOPT}") endif() endforeach() @@ -216,7 +241,6 @@ if(ENABLE_ARES) set(USE_ARES 1) find_package(CARES REQUIRED) list(APPEND CURL_LIBS ${CARES_LIBRARY}) - set(CURL_LIBS ${CURL_LIBS} ${CARES_LIBRARY}) endif() if(0) # This code not needed for building within CMake. @@ -255,6 +279,8 @@ option(CURL_DISABLE_SMTP "to disable SMTP" OFF) mark_as_advanced(CURL_DISABLE_SMTP) option(CURL_DISABLE_GOPHER "to disable Gopher" OFF) mark_as_advanced(CURL_DISABLE_GOPHER) +option(CURL_ENABLE_MQTT "to enable MQTT" OFF) +mark_as_advanced(CURL_ENABLE_MQTT) if(HTTP_ONLY) set(CURL_DISABLE_FTP ON) @@ -267,6 +293,7 @@ if(HTTP_ONLY) set(CURL_DISABLE_RTSP ON) set(CURL_DISABLE_POP3 ON) set(CURL_DISABLE_IMAP ON) + set(CURL_DISABLE_SMB ON) set(CURL_DISABLE_SMTP ON) set(CURL_DISABLE_GOPHER ON) endif() @@ -303,10 +330,6 @@ cmake_dependent_option(ENABLE_MANUAL "to provide the built-in manual" ON "NROFF_USEFUL;PERL_FOUND" OFF) -if(NOT PERL_FOUND) - message(STATUS "Perl not found, testing disabled.") - set(BUILD_TESTING OFF) -endif() if(ENABLE_MANUAL) set(USE_MANUAL ON) endif() @@ -317,6 +340,7 @@ set(CMAKE_C_FLAGS "${CMAKE_ANSI_CFLAGS} ${CMAKE_C_FLAGS}") set(CMAKE_REQUIRED_FLAGS ${CMAKE_ANSI_CFLAGS}) if(CURL_STATIC_CRT) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd") endif() @@ -332,6 +356,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES AIX) endif() # Include all the necessary files for macros +include(CMakePushCheckState) include(CheckFunctionExists) include(CheckIncludeFile) include(CheckIncludeFiles) @@ -359,7 +384,7 @@ endif() # Check for all needed libraries if(0) # This code not needed for building within CMake. -check_library_exists_concat("dl" dlopen HAVE_LIBDL) +check_library_exists_concat("${CMAKE_DL_LIBS}" dlopen HAVE_LIBDL) else() # Use the cmake-defined dl libs as dl is should not be used # on HPUX, but rather dld this avoids a warning @@ -390,7 +415,7 @@ if(WIN32) endif() # check SSL libraries -# TODO support GNUTLS, NSS, POLARSSL, CYASSL +# TODO support GnuTLS if(APPLE) option(CMAKE_USE_SECTRANSP "enable Apple OS native SSL/TLS" OFF) @@ -401,9 +426,12 @@ if(WIN32) CMAKE_USE_WINSSL OFF) endif() option(CMAKE_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF) +option(CMAKE_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF) +option(CMAKE_USE_NSS "Enable NSS for SSL/TLS" OFF) +option(CMAKE_USE_WOLFSSL "enable wolfSSL for SSL/TLS" OFF) set(openssl_default ON) -if(WIN32 OR CMAKE_USE_SECTRANSP OR CMAKE_USE_WINSSL OR CMAKE_USE_MBEDTLS) +if(WIN32 OR CMAKE_USE_SECTRANSP OR CMAKE_USE_WINSSL OR CMAKE_USE_MBEDTLS OR CMAKE_USE_NSS OR CMAKE_USE_WOLFSSL) set(openssl_default OFF) endif() @@ -412,6 +440,9 @@ count_true(enabled_ssl_options_count CMAKE_USE_SECTRANSP CMAKE_USE_OPENSSL CMAKE_USE_MBEDTLS + CMAKE_USE_BEARSSL + CMAKE_USE_NSS + CMAKE_USE_WOLFSSL ) if(enabled_ssl_options_count GREATER "1") set(CURL_WITH_MULTI_SSL ON) @@ -457,8 +488,6 @@ if(CMAKE_USE_OPENSSL) endif() set(SSL_ENABLED ON) set(USE_OPENSSL ON) - set(HAVE_LIBCRYPTO ON) - set(HAVE_LIBSSL ON) list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES}) include_directories(${OPENSSL_INCLUDE_DIR}) @@ -492,6 +521,35 @@ if(CMAKE_USE_MBEDTLS) include_directories(${MBEDTLS_INCLUDE_DIRS}) endif() +if(CMAKE_USE_BEARSSL) + find_package(BearSSL REQUIRED) + set(SSL_ENABLED ON) + set(USE_BEARSSL ON) + list(APPEND CURL_LIBS ${BEARSSL_LIBRARY}) + include_directories(${BEARSSL_INCLUDE_DIRS}) +endif() + +if(CMAKE_USE_WOLFSSL) + find_package(WolfSSL REQUIRED) + set(SSL_ENABLED ON) + set(USE_WOLFSSL ON) + list(APPEND CURL_LIBS ${WolfSSL_LIBRARIES}) + include_directories(${WolfSSL_INCLUDE_DIRS}) +endif() + +if(CMAKE_USE_NSS) + find_package(NSS REQUIRED) + include_directories(${NSS_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${NSS_LIBRARIES}) + set(SSL_ENABLED ON) + set(USE_NSS ON) + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES ${NSS_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_LIBRARIES ${NSS_LIBRARIES}) + check_symbol_exists(PK11_CreateManagedGenericObject "pk11pub.h" HAVE_PK11_CREATEMANAGEDGENERICOBJECT) + cmake_pop_check_state() +endif() + option(USE_NGHTTP2 "Use Nghttp2 library" OFF) if(USE_NGHTTP2) find_package(NGHTTP2 REQUIRED) @@ -499,6 +557,60 @@ if(USE_NGHTTP2) list(APPEND CURL_LIBS ${NGHTTP2_LIBRARIES}) endif() +function(CheckQuicSupportInOpenSSL) + # Be sure that the OpenSSL library actually supports QUIC. + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") + check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD) + message(FATAL_ERROR "QUIC support is missing in OpenSSL/boringssl. Try setting -DOPENSSL_ROOT_DIR") + endif() + cmake_pop_check_state() +endfunction() + +option(USE_NGTCP2 "Use ngtcp2 and nghttp3 libraries for HTTP/3 support" OFF) +if(USE_NGTCP2) + if(USE_OPENSSL) + find_package(NGTCP2 REQUIRED OpenSSL) + CheckQuicSupportInOpenSSL() + elseif(USE_GNUTLS) + # TODO add GnuTLS support as vtls library. + find_package(NGTCP2 REQUIRED GnuTLS) + else() + message(FATAL_ERROR "ngtcp2 requires OpenSSL or GnuTLS") + endif() + set(USE_NGTCP2 ON) + include_directories(${NGTCP2_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${NGTCP2_LIBRARIES}) + + find_package(NGHTTP3 REQUIRED) + set(USE_NGHTTP3 ON) + include_directories(${NGHTTP3_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES}) +endif() + +option(USE_QUICHE "Use quiche library for HTTP/3 support" OFF) +if(USE_QUICHE) + if(USE_NGTCP2) + message(FATAL_ERROR "Only one HTTP/3 backend can be selected!") + endif() + find_package(QUICHE REQUIRED) + CheckQuicSupportInOpenSSL() + set(USE_QUICHE ON) + include_directories(${QUICHE_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${QUICHE_LIBRARIES}) + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES "${QUICHE_INCLUDE_DIRS}") + set(CMAKE_REQUIRED_LIBRARIES "${QUICHE_LIBRARIES}") + check_symbol_exists(quiche_conn_set_qlog_fd "quiche.h" HAVE_QUICHE_CONN_SET_QLOG_FD) + cmake_pop_check_state() +endif() + +if(WIN32) + set(USE_WIN32_CRYPTO ON) +endif() + if(NOT CURL_DISABLE_LDAP) if(WIN32) option(USE_WIN32_LDAP "Use Windows LDAP implementation" ON) @@ -653,7 +765,7 @@ endif() option(CURL_BROTLI "Set to ON to enable building curl with brotli support." OFF) set(HAVE_BROTLI OFF) if(CURL_BROTLI) - find_package(BROTLI QUIET) + find_package(Brotli QUIET) if(BROTLI_FOUND) set(HAVE_BROTLI ON) list(APPEND CURL_LIBS ${BROTLI_LIBRARIES}) @@ -696,6 +808,20 @@ if(CMAKE_USE_LIBSSH2) endif() endif() +# libssh +option(CMAKE_USE_LIBSSH "Use libSSH" OFF) +mark_as_advanced(CMAKE_USE_LIBSSH) +if(NOT HAVE_LIBSSH2 AND CMAKE_USE_LIBSSH) + find_package(libssh CONFIG) + if(libssh_FOUND) + message(STATUS "Found libssh ${libssh_VERSION}") + # Use imported target for include and library paths. + list(APPEND CURL_LIBS ssh) + set(USE_LIBSSH ON) + set(HAVE_LIBSSH_LIBSSH_H 1) + endif() +endif() + option(CMAKE_USE_GSSAPI "Use GSSAPI implementation (right now only Heimdal is supported with CMake build)" OFF) mark_as_advanced(CMAKE_USE_GSSAPI) @@ -764,6 +890,8 @@ else() unset(USE_UNIX_SOCKETS CACHE) endif() +option(ENABLE_ALT_SVC "Enable alt-svc support" OFF) +set(USE_ALTSVC ${ENABLE_ALT_SVC}) if(0) # This code not needed for building within CMake. # @@ -793,7 +921,9 @@ elseif("${CURL_CA_PATH}" STREQUAL "none") unset(CURL_CA_PATH CACHE) elseif("${CURL_CA_PATH}" STREQUAL "auto") unset(CURL_CA_PATH CACHE) - set(CURL_CA_PATH_AUTODETECT TRUE) + if(NOT USE_NSS) + set(CURL_CA_PATH_AUTODETECT TRUE) + endif() else() set(CURL_CA_PATH_SET TRUE) endif() @@ -832,7 +962,7 @@ elseif(CURL_CA_PATH_AUTODETECT OR CURL_CA_BUNDLE_AUTODETECT) endif() if(CURL_CA_PATH_SET AND NOT USE_OPENSSL AND NOT USE_MBEDTLS) - message(FATAL_ERROR + message(STATUS "CA path only supported by OpenSSL, GnuTLS or mbed TLS. " "Set CURL_CA_PATH=none or enable one of those TLS backends.") endif() @@ -876,7 +1006,6 @@ check_include_file_concat("arpa/inet.h" HAVE_ARPA_INET_H) check_include_file_concat("arpa/tftp.h" HAVE_ARPA_TFTP_H) check_include_file_concat("assert.h" HAVE_ASSERT_H) check_include_file_concat("crypto.h" HAVE_CRYPTO_H) -check_include_file_concat("des.h" HAVE_DES_H) check_include_file_concat("err.h" HAVE_ERR_H) check_include_file_concat("errno.h" HAVE_ERRNO_H) check_include_file_concat("fcntl.h" HAVE_FCNTL_H) @@ -925,18 +1054,8 @@ check_include_file_concat("sys/utsname.h" HAVE_SYS_UTSNAME_H) check_type_size(size_t SIZEOF_SIZE_T) check_type_size(ssize_t SIZEOF_SSIZE_T) -check_type_size("long long" SIZEOF_LONG_LONG) -check_type_size("long" SIZEOF_LONG) -check_type_size("short" SIZEOF_SHORT) -check_type_size("int" SIZEOF_INT) -check_type_size("__int64" SIZEOF___INT64) check_type_size("time_t" SIZEOF_TIME_T) -if(HAVE_SIZEOF_LONG_LONG) - set(HAVE_LONGLONG 1) - set(HAVE_LL 1) -endif() - find_file(RANDOM_FILE urandom /dev) mark_as_advanced(RANDOM_FILE) @@ -983,6 +1102,7 @@ check_symbol_exists(strlcat "${CURL_INCLUDES}" HAVE_STRLCAT) check_symbol_exists(getpwuid "${CURL_INCLUDES}" HAVE_GETPWUID) check_symbol_exists(getpwuid_r "${CURL_INCLUDES}" HAVE_GETPWUID_R) check_symbol_exists(geteuid "${CURL_INCLUDES}" HAVE_GETEUID) +check_symbol_exists(usleep "${CURL_INCLUDES}" HAVE_USLEEP) check_symbol_exists(utime "${CURL_INCLUDES}" HAVE_UTIME) check_symbol_exists(gmtime_r "${CURL_INCLUDES}" HAVE_GMTIME_R) check_symbol_exists(localtime_r "${CURL_INCLUDES}" HAVE_LOCALTIME_R) @@ -1010,6 +1130,7 @@ check_symbol_exists(ftruncate "${CURL_INCLUDES}" HAVE_FTRUNCATE) check_symbol_exists(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME) check_symbol_exists(getpeername "${CURL_INCLUDES}" HAVE_GETPEERNAME) check_symbol_exists(getsockname "${CURL_INCLUDES}" HAVE_GETSOCKNAME) +check_symbol_exists(if_nametoindex "${CURL_INCLUDES}" HAVE_IF_NAMETOINDEX) check_symbol_exists(getrlimit "${CURL_INCLUDES}" HAVE_GETRLIMIT) check_symbol_exists(setlocale "${CURL_INCLUDES}" HAVE_SETLOCALE) check_symbol_exists(setmode "${CURL_INCLUDES}" HAVE_SETMODE) @@ -1018,20 +1139,7 @@ check_symbol_exists(fcntl "${CURL_INCLUDES}" HAVE_FCNTL) check_symbol_exists(ioctl "${CURL_INCLUDES}" HAVE_IOCTL) check_symbol_exists(setsockopt "${CURL_INCLUDES}" HAVE_SETSOCKOPT) check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME) - -# symbol exists in win32, but function does not. -if(WIN32) - if(ENABLE_INET_PTON) - check_function_exists(inet_pton HAVE_INET_PTON) - # _WIN32_WINNT_VISTA (0x0600) - add_definitions(-D_WIN32_WINNT=0x0600) - else() - # _WIN32_WINNT_WINXP (0x0501) - add_definitions(-D_WIN32_WINNT=0x0501) - endif() -else() - check_function_exists(inet_pton HAVE_INET_PTON) -endif() +check_symbol_exists(inet_pton "${CURL_INCLUDES}" HAVE_INET_PTON) check_symbol_exists(fsetxattr "${CURL_INCLUDES}" HAVE_FSETXATTR) if(HAVE_FSETXATTR) @@ -1255,6 +1363,23 @@ if(CURL_WERROR) endif() endif() +if(CURL_LTO) + if(CMAKE_VERSION VERSION_LESS 3.9) + message(FATAL_ERROR "Requested LTO 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 CURL_LTO_ERROR LANGUAGES C) + if(CURL_HAS_LTO) + message(STATUS "LTO supported and enabled") + else() + message(FATAL_ERROR "LTO was requested - but compiler doesn't support it\n${CURL_LTO_ERROR}") + endif() +endif() + + # Ugly (but functional) way to include "Makefile.inc" by transforming it (= regenerate it). function(transform_makefile_inc INPUT_FILE OUTPUT_FILE) file(READ ${INPUT_FILE} MAKEFILE_INC_TEXT) @@ -1268,7 +1393,7 @@ function(transform_makefile_inc INPUT_FILE OUTPUT_FILE) string(REGEX REPLACE "\\$\\(([a-zA-Z_][a-zA-Z0-9_]*)\\)" "\${\\1}" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT}) # Replace $() with ${} string(REGEX REPLACE "@([a-zA-Z_][a-zA-Z0-9_]*)@" "\${\\1}" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT}) # Replace @@ with ${}, even if that may not be read by CMake scripts. file(WRITE ${OUTPUT_FILE} ${MAKEFILE_INC_TEXT}) - + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${INPUT_FILE}") endfunction() if(0) # This code not needed for building within CMake. @@ -1304,19 +1429,29 @@ install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmcurl) #----------------------------------------------------------------------------- if(0) # This code not needed for building within CMake. -include(CTest) -if(BUILD_TESTING) +option(BUILD_TESTING "Build tests" "${PERL_FOUND}") +if(NOT PERL_FOUND) + message(STATUS "Perl not found, testing disabled.") +elseif(BUILD_TESTING) add_subdirectory(tests) endif() +# NTLM support requires crypto function adaptions from various SSL libs +# TODO alternative SSL libs tests for SSP1, GNUTLS, NSS +if(NOT CURL_DISABLE_CRYPTO_AUTH AND (USE_OPENSSL OR USE_WINDOWS_SSPI OR USE_DARWINSSL OR USE_MBEDTLS OR USE_WIN32_CRYPTO)) + set(use_ntlm ON) +else() + set(use_ntlm OFF) +endif() + # Helper to populate a list (_items) with a label when conditions (the remaining # args) are satisfied -function(_add_if label) - # TODO need to disable policy CMP0054 (CMake 3.1) to allow this indirection +macro(_add_if label) + # needs to be a macro to allow this indirection if(${ARGN}) - set(_items ${_items} "${label}" PARENT_SCOPE) + set(_items ${_items} "${label}") endif() -endfunction() +endmacro() # Clear list and try to detect available features set(_items) @@ -1331,6 +1466,7 @@ _add_if("Largefile" (CURL_SIZEOF_CURL_OFF_T GREATER 4) AND # TODO SSP1 (WinSSL) check is missing _add_if("SSPI" USE_WINDOWS_SSPI) _add_if("GSS-API" HAVE_GSSAPI) +_add_if("alt-svc" ENABLE_ALT_SVC) # TODO SSP1 missing for SPNEGO _add_if("SPNEGO" NOT CURL_DISABLE_CRYPTO_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) @@ -1338,15 +1474,16 @@ _add_if("Kerberos" NOT CURL_DISABLE_CRYPTO_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) # NTLM support requires crypto function adaptions from various SSL libs # TODO alternative SSL libs tests for SSP1, GNUTLS, NSS -if(NOT CURL_DISABLE_CRYPTO_AUTH AND (USE_OPENSSL OR USE_WINDOWS_SSPI OR USE_SECTRANSP OR USE_MBEDTLS)) - _add_if("NTLM" 1) - # TODO missing option (autoconf: --enable-ntlm-wb) - _add_if("NTLM_WB" NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED) -endif() +_add_if("NTLM" use_ntlm) +# TODO missing option (autoconf: --enable-ntlm-wb) +_add_if("NTLM_WB" use_ntlm AND NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED) # TODO missing option (--enable-tls-srp), depends on GNUTLS_SRP/OPENSSL_SRP _add_if("TLS-SRP" USE_TLS_SRP) # TODO option --with-nghttp2 tests for nghttp2 lib and nghttp2/nghttp2.h header _add_if("HTTP2" USE_NGHTTP2) +_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) +_add_if("MultiSSL" CURL_WITH_MULTI_SSL) +_add_if("HTTPS-proxy" SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS OR USE_NSS)) string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") message(STATUS "Enabled features: ${SUPPORT_FEATURES}") @@ -1371,12 +1508,15 @@ _add_if("POP3" NOT CURL_DISABLE_POP3) _add_if("POP3S" NOT CURL_DISABLE_POP3 AND SSL_ENABLED) _add_if("IMAP" NOT CURL_DISABLE_IMAP) _add_if("IMAPS" NOT CURL_DISABLE_IMAP AND SSL_ENABLED) +_add_if("SMB" NOT CURL_DISABLE_SMB AND use_ntlm) +_add_if("SMBS" NOT CURL_DISABLE_SMB AND SSL_ENABLED AND use_ntlm) _add_if("SMTP" NOT CURL_DISABLE_SMTP) _add_if("SMTPS" NOT CURL_DISABLE_SMTP AND SSL_ENABLED) -_add_if("SCP" USE_LIBSSH2) -_add_if("SFTP" USE_LIBSSH2) +_add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH) +_add_if("SFTP" USE_LIBSSH2 OR USE_LIBSSH) _add_if("RTSP" NOT CURL_DISABLE_RTSP) _add_if("RTMP" USE_LIBRTMP) +_add_if("MQTT" CURL_ENABLE_MQTT) if(_items) list(SORT _items) endif() @@ -1389,6 +1529,9 @@ _add_if("WinSSL" SSL_ENABLED AND USE_WINDOWS_SSPI) _add_if("OpenSSL" SSL_ENABLED AND USE_OPENSSL) _add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP) _add_if("mbedTLS" SSL_ENABLED AND USE_MBEDTLS) +_add_if("BearSSL" SSL_ENABLED AND USE_BEARSSL) +_add_if("NSS" SSL_ENABLED AND USE_NSS) +_add_if("wolfSSL" SSL_ENABLED AND USE_WOLFSSL) if(_items) list(SORT _items) endif() @@ -1402,25 +1545,43 @@ set(CONFIGURE_OPTIONS "") # TODO when to set "-DCURL_STATICLIB" for CPPFLAG_CURL_STATICLIB? set(CPPFLAG_CURL_STATICLIB "") set(CURLVERSION "${CURL_VERSION}") -if(BUILD_SHARED_LIBS) - set(ENABLE_SHARED "yes") - set(ENABLE_STATIC "no") -else() - set(ENABLE_SHARED "no") - set(ENABLE_STATIC "yes") -endif() set(exec_prefix "\${prefix}") set(includedir "\${prefix}/include") set(LDFLAGS "${CMAKE_SHARED_LINKER_FLAGS}") set(LIBCURL_LIBS "") set(libdir "${CMAKE_INSTALL_PREFIX}/lib") foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS}) + if(TARGET "${_lib}") + set(_libname "${_lib}") + get_target_property(_libtype "${_libname}" TYPE) + if(_libtype STREQUAL INTERFACE_LIBRARY) + # Interface libraries can occur when an external project embeds curl and + # defined targets such as ZLIB::ZLIB by themselves. Ignore these as + # reading the LOCATION property will error out. Assume the user won't need + # this information in the .pc file. + continue() + endif() + get_target_property(_lib "${_libname}" LOCATION) + if(NOT _lib) + message(WARNING "Bad lib in library list: ${_libname}") + continue() + endif() + endif() if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-") set(LIBCURL_LIBS "${LIBCURL_LIBS} ${_lib}") else() set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_lib}") endif() endforeach() +if(BUILD_SHARED_LIBS) + set(ENABLE_SHARED "yes") + set(ENABLE_STATIC "no") + set(LIBCURL_NO_SHARED "") +else() + set(ENABLE_SHARED "no") + set(ENABLE_STATIC "yes") + set(LIBCURL_NO_SHARED "${LIBCURL_LIBS}") +endif() # "a" (Linux) or "lib" (Windows) string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}") set(prefix "${CMAKE_INSTALL_PREFIX}") diff --git a/Utilities/cmcurl/COPYING b/Utilities/cmcurl/COPYING index 3528bd75663..9d9e4af8d8b 100644 --- a/Utilities/cmcurl/COPYING +++ b/Utilities/cmcurl/COPYING @@ -1,6 +1,6 @@ COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2019, Daniel Stenberg, , and many +Copyright (c) 1996 - 2020, Daniel Stenberg, , and many contributors, see the THANKS file. All rights reserved. diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index 089c427db13..194b57811e4 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h @@ -1,5 +1,5 @@ -#ifndef __CURL_CURL_H -#define __CURL_CURL_H +#ifndef CURLINC_CURL_H +#define CURLINC_CURL_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -38,12 +38,12 @@ #include "system.h" /* determine things run-time */ /* - * Define WIN32 when build target is Win32 API + * Define CURL_WIN32 when build target is Win32 API */ -#if (defined(_WIN32) || defined(__WIN32__)) && \ - !defined(WIN32) && !defined(__SYMBIAN32__) -#define WIN32 +#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) && \ + !defined(__SYMBIAN32__) +#define CURL_WIN32 #endif #include @@ -58,7 +58,7 @@ #include #include -#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) +#if defined(CURL_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) #if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \ defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)) /* The check above prevents the winsock2 inclusion if winsock.h already was @@ -79,11 +79,11 @@ #include #endif -#if !defined(WIN32) && !defined(_WIN32_WCE) +#if !defined(CURL_WIN32) && !defined(_WIN32_WCE) #include #endif -#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) +#if !defined(CURL_WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) #include #endif @@ -114,7 +114,7 @@ typedef void CURLSH; #ifdef CURL_STATICLIB # define CURL_EXTERN -#elif defined(WIN32) || defined(__SYMBIAN32__) || \ +#elif defined(CURL_WIN32) || defined(__SYMBIAN32__) || \ (__has_declspec_attribute(dllexport) && \ __has_declspec_attribute(dllimport)) # if defined(BUILDING_LIBCURL) @@ -130,7 +130,7 @@ typedef void CURLSH; #ifndef curl_socket_typedef /* socket typedef */ -#if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) +#if defined(CURL_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) typedef SOCKET curl_socket_t; #define CURL_SOCKET_BAD INVALID_SOCKET #else @@ -154,7 +154,8 @@ typedef enum { CURLSSLBACKEND_SECURETRANSPORT = 9, CURLSSLBACKEND_AXTLS = 10, /* never used since 7.63.0 */ CURLSSLBACKEND_MBEDTLS = 11, - CURLSSLBACKEND_MESALINK = 12 + CURLSSLBACKEND_MESALINK = 12, + CURLSSLBACKEND_BEARSSL = 13 } curl_sslbackend; /* aliases for library clones and renames */ @@ -209,16 +210,22 @@ struct curl_httppost { set. Added in 7.46.0 */ }; -/* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered - deprecated but was the only choice up until 7.31.0 */ + +/* This is a return code for the progress callback that, when returned, will + signal libcurl to continue executing the default progress function */ +#define CURL_PROGRESSFUNC_CONTINUE 0x10000001 + +/* This is the CURLOPT_PROGRESSFUNCTION callback prototype. It is now + considered deprecated but was the only choice up until 7.31.0 */ typedef int (*curl_progress_callback)(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); -/* This is the CURLOPT_XFERINFOFUNCTION callback proto. It was introduced in - 7.32.0, it avoids floating point and provides more detailed information. */ +/* This is the CURLOPT_XFERINFOFUNCTION callback prototype. It was introduced + in 7.32.0, avoids the use of floating point numbers and provides more + detailed information. */ typedef int (*curl_xferinfo_callback)(void *clientp, curl_off_t dltotal, curl_off_t dlnow, @@ -283,10 +290,7 @@ typedef enum { #define CURLFINFOFLAG_KNOWN_SIZE (1<<6) #define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) -/* Content of this structure depends on information which is known and is - achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man - page for callbacks returning this structure -- some fields are mandatory, - some others are optional. The FLAG field has special meaning. */ +/* Information about a single file, used when doing FTP wildcard matching */ struct curl_fileinfo { char *filename; curlfiletype filetype; @@ -602,6 +606,10 @@ typedef enum { */ CURLE_RECURSIVE_API_CALL, /* 93 - an api function was called from inside a callback */ + CURLE_AUTH_ERROR, /* 94 - an authentication function returned an + error */ + CURLE_HTTP3, /* 95 - An HTTP/3 layer problem */ + CURLE_QUIC_CONNECT_ERROR, /* 96 - QUIC connection error */ CURL_LAST /* never use! */ } CURLcode; @@ -685,8 +693,10 @@ typedef enum { typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ - void *ssl_ctx, /* actually an - OpenSSL SSL_CTX */ + void *ssl_ctx, /* actually an OpenSSL + or WolfSSL SSL_CTX, + or an mbedTLS + mbedtls_ssl_config */ void *userptr); typedef enum { @@ -764,7 +774,7 @@ enum curl_khtype { }; struct curl_khkey { - const char *key; /* points to a zero-terminated string encoded with base64 + const char *key; /* points to a null-terminated string encoded with base64 if len is zero, otherwise to the "raw" data */ size_t len; enum curl_khtype keytype; @@ -819,6 +829,19 @@ typedef enum { SSL backends where such behavior is present. */ #define CURLSSLOPT_NO_REVOKE (1<<1) +/* - NO_PARTIALCHAIN tells libcurl to *NOT* accept a partial certificate chain + if possible. The OpenSSL backend has this ability. */ +#define CURLSSLOPT_NO_PARTIALCHAIN (1<<2) + +/* - REVOKE_BEST_EFFORT tells libcurl to ignore certificate revocation offline + checks and ignore missing revocation list for those SSL backends where such + behavior is present. */ +#define CURLSSLOPT_REVOKE_BEST_EFFORT (1<<3) + +/* - CURLSSLOPT_NATIVE_CA tells libcurl to use standard certificate store of + operating system. Currently implemented under MS-Windows. */ +#define CURLSSLOPT_NATIVE_CA (1<<4) + /* The default connection attempt delay in milliseconds for happy eyeballs. CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document this value, keep them in sync. */ @@ -883,7 +906,7 @@ typedef enum { /* CURLALTSVC_* are bits for the CURLOPT_ALTSVC_CTRL option */ #define CURLALTSVC_IMMEDIATELY (1<<0) -#define CURLALTSVC_ALTUSED (1<<1) + #define CURLALTSVC_READONLYFILE (1<<2) #define CURLALTSVC_H1 (1<<3) #define CURLALTSVC_H2 (1<<4) @@ -918,85 +941,72 @@ typedef enum { #define CURLPROTO_GOPHER (1<<25) #define CURLPROTO_SMB (1<<26) #define CURLPROTO_SMBS (1<<27) +#define CURLPROTO_MQTT (1<<28) #define CURLPROTO_ALL (~0) /* enable everything */ /* long may be 32 or 64 bits, but we should never depend on anything else but 32 */ #define CURLOPTTYPE_LONG 0 #define CURLOPTTYPE_OBJECTPOINT 10000 -#define CURLOPTTYPE_STRINGPOINT 10000 #define CURLOPTTYPE_FUNCTIONPOINT 20000 #define CURLOPTTYPE_OFF_T 30000 +#define CURLOPTTYPE_BLOB 40000 /* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the string options from the header file */ -/* name is uppercase CURLOPT_, - type is one of the defined CURLOPTTYPE_ - number is unique identifier */ -#ifdef CINIT -#undef CINIT -#endif -#ifdef CURL_ISOCPP -#define CINIT(na,t,nu) CURLOPT_ ## na = CURLOPTTYPE_ ## t + nu -#else -/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ -#define LONG CURLOPTTYPE_LONG -#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT -#define STRINGPOINT CURLOPTTYPE_OBJECTPOINT -#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT -#define OFF_T CURLOPTTYPE_OFF_T -#define CINIT(name,type,number) CURLOPT_/**/name = type + number -#endif +#define CURLOPT(na,t,nu) na = t + nu + +/* handy aliases that make no run-time difference */ +#define CURLOPTTYPE_STRINGPOINT CURLOPTTYPE_OBJECTPOINT +#define CURLOPTTYPE_SLISTPOINT CURLOPTTYPE_OBJECTPOINT /* - * This macro-mania below setups the CURLOPT_[what] enum, to be used with - * curl_easy_setopt(). The first argument in the CINIT() macro is the [what] - * word. + * All CURLOPT_* values. */ typedef enum { /* This is the FILE * or void * the regular output should be written to. */ - CINIT(WRITEDATA, OBJECTPOINT, 1), + CURLOPT(CURLOPT_WRITEDATA, CURLOPTTYPE_OBJECTPOINT, 1), /* The full URL to get/put */ - CINIT(URL, STRINGPOINT, 2), + CURLOPT(CURLOPT_URL, CURLOPTTYPE_STRINGPOINT, 2), /* Port number to connect to, if other than default. */ - CINIT(PORT, LONG, 3), + CURLOPT(CURLOPT_PORT, CURLOPTTYPE_LONG, 3), /* Name of proxy to use. */ - CINIT(PROXY, STRINGPOINT, 4), + CURLOPT(CURLOPT_PROXY, CURLOPTTYPE_STRINGPOINT, 4), /* "user:password;options" to use when fetching. */ - CINIT(USERPWD, STRINGPOINT, 5), + CURLOPT(CURLOPT_USERPWD, CURLOPTTYPE_STRINGPOINT, 5), /* "user:password" to use with proxy. */ - CINIT(PROXYUSERPWD, STRINGPOINT, 6), + CURLOPT(CURLOPT_PROXYUSERPWD, CURLOPTTYPE_STRINGPOINT, 6), /* Range to get, specified as an ASCII string. */ - CINIT(RANGE, STRINGPOINT, 7), + CURLOPT(CURLOPT_RANGE, CURLOPTTYPE_STRINGPOINT, 7), /* not used */ /* Specified file stream to upload from (use as input): */ - CINIT(READDATA, OBJECTPOINT, 9), + CURLOPT(CURLOPT_READDATA, CURLOPTTYPE_OBJECTPOINT, 9), /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE * bytes big. */ - CINIT(ERRORBUFFER, OBJECTPOINT, 10), + 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. */ - CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + 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. */ - CINIT(READFUNCTION, FUNCTIONPOINT, 12), + CURLOPT(CURLOPT_READFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 12), /* Time-out the read operation after this amount of seconds */ - CINIT(TIMEOUT, LONG, 13), + CURLOPT(CURLOPT_TIMEOUT, CURLOPTTYPE_LONG, 13), /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about * how large the file being sent really is. That allows better error @@ -1007,20 +1017,20 @@ typedef enum { * which takes an off_t type, allowing platforms with larger off_t * sizes to handle larger files. See below for INFILESIZE_LARGE. */ - CINIT(INFILESIZE, LONG, 14), + CURLOPT(CURLOPT_INFILESIZE, CURLOPTTYPE_LONG, 14), /* POST static input fields. */ - CINIT(POSTFIELDS, OBJECTPOINT, 15), + CURLOPT(CURLOPT_POSTFIELDS, CURLOPTTYPE_OBJECTPOINT, 15), /* Set the referrer page (needed by some CGIs) */ - CINIT(REFERER, STRINGPOINT, 16), + CURLOPT(CURLOPT_REFERER, CURLOPTTYPE_STRINGPOINT, 16), /* Set the FTP PORT string (interface name, named or numerical IP address) Use i.e '-' to use default address. */ - CINIT(FTPPORT, STRINGPOINT, 17), + CURLOPT(CURLOPT_FTPPORT, CURLOPTTYPE_STRINGPOINT, 17), /* Set the User-Agent string (examined by some CGIs) */ - CINIT(USERAGENT, STRINGPOINT, 18), + CURLOPT(CURLOPT_USERAGENT, CURLOPTTYPE_STRINGPOINT, 18), /* If the download receives less than "low speed limit" bytes/second * during "low speed time" seconds, the operations is aborted. @@ -1029,10 +1039,10 @@ typedef enum { */ /* Set the "low speed limit" */ - CINIT(LOW_SPEED_LIMIT, LONG, 19), + CURLOPT(CURLOPT_LOW_SPEED_LIMIT, CURLOPTTYPE_LONG, 19), /* Set the "low speed time" */ - CINIT(LOW_SPEED_TIME, LONG, 20), + CURLOPT(CURLOPT_LOW_SPEED_TIME, CURLOPTTYPE_LONG, 20), /* Set the continuation offset. * @@ -1040,48 +1050,48 @@ typedef enum { * off_t types, allowing for large file offsets on platforms which * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. */ - CINIT(RESUME_FROM, LONG, 21), + CURLOPT(CURLOPT_RESUME_FROM, CURLOPTTYPE_LONG, 21), /* Set cookie in request: */ - CINIT(COOKIE, STRINGPOINT, 22), + CURLOPT(CURLOPT_COOKIE, CURLOPTTYPE_STRINGPOINT, 22), /* This points to a linked list of headers, struct curl_slist kind. This list is also used for RTSP (in spite of its name) */ - CINIT(HTTPHEADER, OBJECTPOINT, 23), + CURLOPT(CURLOPT_HTTPHEADER, CURLOPTTYPE_SLISTPOINT, 23), /* This points to a linked list of post entries, struct curl_httppost */ - CINIT(HTTPPOST, OBJECTPOINT, 24), + CURLOPT(CURLOPT_HTTPPOST, CURLOPTTYPE_OBJECTPOINT, 24), /* name of the file keeping your private SSL-certificate */ - CINIT(SSLCERT, STRINGPOINT, 25), + CURLOPT(CURLOPT_SSLCERT, CURLOPTTYPE_STRINGPOINT, 25), /* password for the SSL or SSH private key */ - CINIT(KEYPASSWD, STRINGPOINT, 26), + CURLOPT(CURLOPT_KEYPASSWD, CURLOPTTYPE_STRINGPOINT, 26), /* send TYPE parameter? */ - CINIT(CRLF, LONG, 27), + CURLOPT(CURLOPT_CRLF, CURLOPTTYPE_LONG, 27), /* send linked-list of QUOTE commands */ - CINIT(QUOTE, OBJECTPOINT, 28), + CURLOPT(CURLOPT_QUOTE, CURLOPTTYPE_SLISTPOINT, 28), /* send FILE * or void * to store headers to, if you use a callback it is simply passed to the callback unmodified */ - CINIT(HEADERDATA, OBJECTPOINT, 29), + CURLOPT(CURLOPT_HEADERDATA, CURLOPTTYPE_OBJECTPOINT, 29), /* point to a file to read the initial cookies from, also enables "cookie awareness" */ - CINIT(COOKIEFILE, STRINGPOINT, 31), + CURLOPT(CURLOPT_COOKIEFILE, CURLOPTTYPE_STRINGPOINT, 31), /* What version to specifically try to use. See CURL_SSLVERSION defines below. */ - CINIT(SSLVERSION, LONG, 32), + CURLOPT(CURLOPT_SSLVERSION, CURLOPTTYPE_LONG, 32), /* What kind of HTTP time condition to use, see defines */ - CINIT(TIMECONDITION, LONG, 33), + CURLOPT(CURLOPT_TIMECONDITION, CURLOPTTYPE_LONG, 33), /* Time to use with the above condition. Specified in number of seconds since 1 Jan 1970 */ - CINIT(TIMEVALUE, LONG, 34), + CURLOPT(CURLOPT_TIMEVALUE, CURLOPTTYPE_LONG, 34), /* 35 = OBSOLETE */ @@ -1089,37 +1099,58 @@ typedef enum { HTTP: DELETE, TRACE and others FTP: to use a different list command */ - CINIT(CUSTOMREQUEST, STRINGPOINT, 36), + CURLOPT(CURLOPT_CUSTOMREQUEST, CURLOPTTYPE_STRINGPOINT, 36), /* FILE handle to use instead of stderr */ - CINIT(STDERR, OBJECTPOINT, 37), + CURLOPT(CURLOPT_STDERR, CURLOPTTYPE_OBJECTPOINT, 37), /* 38 is not used */ /* send linked-list of post-transfer QUOTE commands */ - CINIT(POSTQUOTE, OBJECTPOINT, 39), + CURLOPT(CURLOPT_POSTQUOTE, CURLOPTTYPE_SLISTPOINT, 39), + + /* OBSOLETE, do not use! */ + CURLOPT(CURLOPT_OBSOLETE40, CURLOPTTYPE_OBJECTPOINT, 40), + + /* talk a lot */ + CURLOPT(CURLOPT_VERBOSE, CURLOPTTYPE_LONG, 41), + + /* throw the header out too */ + CURLOPT(CURLOPT_HEADER, CURLOPTTYPE_LONG, 42), + + /* shut off the progress meter */ + CURLOPT(CURLOPT_NOPROGRESS, CURLOPTTYPE_LONG, 43), + + /* use HEAD to get http document */ + CURLOPT(CURLOPT_NOBODY, CURLOPTTYPE_LONG, 44), + + /* no output on http error codes >= 400 */ + CURLOPT(CURLOPT_FAILONERROR, CURLOPTTYPE_LONG, 45), + + /* this is an upload */ + CURLOPT(CURLOPT_UPLOAD, CURLOPTTYPE_LONG, 46), - CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */ + /* HTTP POST method */ + CURLOPT(CURLOPT_POST, CURLOPTTYPE_LONG, 47), - CINIT(VERBOSE, LONG, 41), /* talk a lot */ - CINIT(HEADER, LONG, 42), /* throw the header out too */ - CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ - CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ - CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 400 */ - CINIT(UPLOAD, LONG, 46), /* this is an upload */ - CINIT(POST, LONG, 47), /* HTTP POST method */ - CINIT(DIRLISTONLY, LONG, 48), /* bare names when listing directories */ + /* bare names when listing directories */ + CURLOPT(CURLOPT_DIRLISTONLY, CURLOPTTYPE_LONG, 48), - CINIT(APPEND, LONG, 50), /* Append instead of overwrite on upload! */ + /* Append instead of overwrite on upload! */ + CURLOPT(CURLOPT_APPEND, CURLOPTTYPE_LONG, 50), /* Specify whether to read the user+password from the .netrc or the URL. * This must be one of the CURL_NETRC_* enums below. */ - CINIT(NETRC, LONG, 51), + CURLOPT(CURLOPT_NETRC, CURLOPTTYPE_LONG, 51), - CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + /* use Location: Luke! */ + CURLOPT(CURLOPT_FOLLOWLOCATION, CURLOPTTYPE_LONG, 52), - CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ - CINIT(PUT, LONG, 54), /* HTTP PUT */ + /* transfer data in text/ASCII format */ + CURLOPT(CURLOPT_TRANSFERTEXT, CURLOPTTYPE_LONG, 53), + + /* HTTP PUT */ + CURLOPT(CURLOPT_PUT, CURLOPTTYPE_LONG, 54), /* 55 = OBSOLETE */ @@ -1127,265 +1158,267 @@ typedef enum { * Function that will be called instead of the internal progress display * function. This function should be defined as the curl_progress_callback * prototype defines. */ - CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + CURLOPT(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56), /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION callbacks */ - CINIT(PROGRESSDATA, OBJECTPOINT, 57), + CURLOPT(CURLOPT_PROGRESSDATA, CURLOPTTYPE_OBJECTPOINT, 57), #define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA /* We want the referrer field set automatically when following locations */ - CINIT(AUTOREFERER, LONG, 58), + CURLOPT(CURLOPT_AUTOREFERER, CURLOPTTYPE_LONG, 58), /* Port of the proxy, can be set in the proxy string as well with: "[host]:[port]" */ - CINIT(PROXYPORT, LONG, 59), + CURLOPT(CURLOPT_PROXYPORT, CURLOPTTYPE_LONG, 59), /* size of the POST input data, if strlen() is not good to use */ - CINIT(POSTFIELDSIZE, LONG, 60), + CURLOPT(CURLOPT_POSTFIELDSIZE, CURLOPTTYPE_LONG, 60), /* tunnel non-http operations through a HTTP proxy */ - CINIT(HTTPPROXYTUNNEL, LONG, 61), + CURLOPT(CURLOPT_HTTPPROXYTUNNEL, CURLOPTTYPE_LONG, 61), /* Set the interface string to use as outgoing network interface */ - CINIT(INTERFACE, STRINGPOINT, 62), + CURLOPT(CURLOPT_INTERFACE, CURLOPTTYPE_STRINGPOINT, 62), /* 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 doesn't match one of these, 'private' will be used. */ - CINIT(KRBLEVEL, STRINGPOINT, 63), + CURLOPT(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63), /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ - CINIT(SSL_VERIFYPEER, LONG, 64), + CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64), /* The CApath or CAfile used to validate the peer certificate this option is used only if SSL_VERIFYPEER is true */ - CINIT(CAINFO, STRINGPOINT, 65), + CURLOPT(CURLOPT_CAINFO, CURLOPTTYPE_STRINGPOINT, 65), /* 66 = OBSOLETE */ /* 67 = OBSOLETE */ /* Maximum number of http redirects to follow */ - CINIT(MAXREDIRS, LONG, 68), + CURLOPT(CURLOPT_MAXREDIRS, CURLOPTTYPE_LONG, 68), /* Pass a long set to 1 to get the date of the requested document (if possible)! Pass a zero to shut it off. */ - CINIT(FILETIME, LONG, 69), + CURLOPT(CURLOPT_FILETIME, CURLOPTTYPE_LONG, 69), /* This points to a linked list of telnet options */ - CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + CURLOPT(CURLOPT_TELNETOPTIONS, CURLOPTTYPE_SLISTPOINT, 70), /* Max amount of cached alive connections */ - CINIT(MAXCONNECTS, LONG, 71), + CURLOPT(CURLOPT_MAXCONNECTS, CURLOPTTYPE_LONG, 71), - CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */ + /* OBSOLETE, do not use! */ + CURLOPT(CURLOPT_OBSOLETE72, CURLOPTTYPE_LONG, 72), /* 73 = OBSOLETE */ /* Set to explicitly use a new connection for the upcoming transfer. Do not use this unless you're absolutely sure of this, as it makes the operation slower and is less friendly for the network. */ - CINIT(FRESH_CONNECT, LONG, 74), + CURLOPT(CURLOPT_FRESH_CONNECT, CURLOPTTYPE_LONG, 74), /* Set to explicitly forbid the upcoming transfer's connection to be re-used when done. Do not use this unless you're absolutely sure of this, as it makes the operation slower and is less friendly for the network. */ - CINIT(FORBID_REUSE, LONG, 75), + CURLOPT(CURLOPT_FORBID_REUSE, CURLOPTTYPE_LONG, 75), /* Set to a file name that contains random data for libcurl to use to seed the random engine when doing SSL connects. */ - CINIT(RANDOM_FILE, STRINGPOINT, 76), + CURLOPT(CURLOPT_RANDOM_FILE, CURLOPTTYPE_STRINGPOINT, 76), /* Set to the Entropy Gathering Daemon socket pathname */ - CINIT(EGDSOCKET, STRINGPOINT, 77), + CURLOPT(CURLOPT_EGDSOCKET, CURLOPTTYPE_STRINGPOINT, 77), /* Time-out connect operations after this amount of seconds, if connects are OK within this time, then fine... This only aborts the connect phase. */ - CINIT(CONNECTTIMEOUT, LONG, 78), + 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. */ - CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + CURLOPT(CURLOPT_HEADERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 79), /* Set this to force the HTTP request to get back to GET. Only really usable if POST, PUT or a custom request have been used first. */ - CINIT(HTTPGET, LONG, 80), + CURLOPT(CURLOPT_HTTPGET, CURLOPTTYPE_LONG, 80), /* Set if we should verify the Common name from the peer certificate in ssl * handshake, set 1 to check existence, 2 to ensure that it matches the * provided hostname. */ - CINIT(SSL_VERIFYHOST, LONG, 81), + CURLOPT(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81), /* Specify which file name to write all known cookies in after completed operation. Set file name to "-" (dash) to make it go to stdout. */ - CINIT(COOKIEJAR, STRINGPOINT, 82), + CURLOPT(CURLOPT_COOKIEJAR, CURLOPTTYPE_STRINGPOINT, 82), /* Specify which SSL ciphers to use */ - CINIT(SSL_CIPHER_LIST, STRINGPOINT, 83), + CURLOPT(CURLOPT_SSL_CIPHER_LIST, CURLOPTTYPE_STRINGPOINT, 83), /* Specify which HTTP version to use! This must be set to one of the CURL_HTTP_VERSION* enums set below. */ - CINIT(HTTP_VERSION, LONG, 84), + CURLOPT(CURLOPT_HTTP_VERSION, CURLOPTTYPE_LONG, 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 PASV command. */ - CINIT(FTP_USE_EPSV, LONG, 85), + CURLOPT(CURLOPT_FTP_USE_EPSV, CURLOPTTYPE_LONG, 85), /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ - CINIT(SSLCERTTYPE, STRINGPOINT, 86), + CURLOPT(CURLOPT_SSLCERTTYPE, CURLOPTTYPE_STRINGPOINT, 86), /* name of the file keeping your private SSL-key */ - CINIT(SSLKEY, STRINGPOINT, 87), + CURLOPT(CURLOPT_SSLKEY, CURLOPTTYPE_STRINGPOINT, 87), /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ - CINIT(SSLKEYTYPE, STRINGPOINT, 88), + CURLOPT(CURLOPT_SSLKEYTYPE, CURLOPTTYPE_STRINGPOINT, 88), /* crypto engine for the SSL-sub system */ - CINIT(SSLENGINE, STRINGPOINT, 89), + CURLOPT(CURLOPT_SSLENGINE, CURLOPTTYPE_STRINGPOINT, 89), /* set the crypto engine for the SSL-sub system as default the param has no meaning... */ - CINIT(SSLENGINE_DEFAULT, LONG, 90), + CURLOPT(CURLOPT_SSLENGINE_DEFAULT, CURLOPTTYPE_LONG, 90), /* Non-zero value means to use the global dns cache */ - CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */ + /* DEPRECATED, do not use! */ + CURLOPT(CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOPTTYPE_LONG, 91), /* DNS cache timeout */ - CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + CURLOPT(CURLOPT_DNS_CACHE_TIMEOUT, CURLOPTTYPE_LONG, 92), /* send linked-list of pre-transfer QUOTE commands */ - CINIT(PREQUOTE, OBJECTPOINT, 93), + CURLOPT(CURLOPT_PREQUOTE, CURLOPTTYPE_SLISTPOINT, 93), /* set the debug function */ - CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + CURLOPT(CURLOPT_DEBUGFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 94), /* set the data for the debug function */ - CINIT(DEBUGDATA, OBJECTPOINT, 95), + CURLOPT(CURLOPT_DEBUGDATA, CURLOPTTYPE_OBJECTPOINT, 95), /* mark this as start of a cookie session */ - CINIT(COOKIESESSION, LONG, 96), + CURLOPT(CURLOPT_COOKIESESSION, CURLOPTTYPE_LONG, 96), /* The CApath directory used to validate the peer certificate this option is used only if SSL_VERIFYPEER is true */ - CINIT(CAPATH, STRINGPOINT, 97), + CURLOPT(CURLOPT_CAPATH, CURLOPTTYPE_STRINGPOINT, 97), /* Instruct libcurl to use a smaller receive buffer */ - CINIT(BUFFERSIZE, LONG, 98), + CURLOPT(CURLOPT_BUFFERSIZE, CURLOPTTYPE_LONG, 98), /* Instruct libcurl to not use any signal/alarm handlers, even when using timeouts. This option is useful for multi-threaded applications. See libcurl-the-guide for more background information. */ - CINIT(NOSIGNAL, LONG, 99), + CURLOPT(CURLOPT_NOSIGNAL, CURLOPTTYPE_LONG, 99), /* Provide a CURLShare for mutexing non-ts data */ - CINIT(SHARE, OBJECTPOINT, 100), + CURLOPT(CURLOPT_SHARE, CURLOPTTYPE_OBJECTPOINT, 100), /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ - CINIT(PROXYTYPE, LONG, 101), + CURLOPT(CURLOPT_PROXYTYPE, CURLOPTTYPE_LONG, 101), /* Set the Accept-Encoding string. Use this to tell a server you would like the response to be compressed. Before 7.21.6, this was known as CURLOPT_ENCODING */ - CINIT(ACCEPT_ENCODING, STRINGPOINT, 102), + CURLOPT(CURLOPT_ACCEPT_ENCODING, CURLOPTTYPE_STRINGPOINT, 102), /* Set pointer to private data */ - CINIT(PRIVATE, OBJECTPOINT, 103), + CURLOPT(CURLOPT_PRIVATE, CURLOPTTYPE_OBJECTPOINT, 103), /* Set aliases for HTTP 200 in the HTTP Response header */ - CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + CURLOPT(CURLOPT_HTTP200ALIASES, CURLOPTTYPE_SLISTPOINT, 104), /* Continue to send authentication (user+password) when following locations, even when hostname changed. This can potentially send off the name and password to whatever host the server decides. */ - CINIT(UNRESTRICTED_AUTH, LONG, 105), + 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 attempted before the good old traditional PORT command. */ - CINIT(FTP_USE_EPRT, LONG, 106), + CURLOPT(CURLOPT_FTP_USE_EPRT, CURLOPTTYPE_LONG, 106), /* Set this to a bitmask value to enable the particular authentications methods you like. Use this in combination with CURLOPT_USERPWD. Note that setting multiple bits may cause extra network round-trips. */ - CINIT(HTTPAUTH, LONG, 107), + CURLOPT(CURLOPT_HTTPAUTH, CURLOPTTYPE_LONG, 107), - /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx - in second argument. The function must be matching the - curl_ssl_ctx_callback proto. */ - CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108), + /* Set the ssl context callback function, currently only for OpenSSL or + WolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument. + The function must match the curl_ssl_ctx_callback prototype. */ + CURLOPT(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108), /* Set the userdata for the ssl context callback function's third argument */ - CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + CURLOPT(CURLOPT_SSL_CTX_DATA, CURLOPTTYPE_OBJECTPOINT, 109), /* FTP Option that causes missing dirs to be created on the remote server. In 7.19.4 we introduced the convenience enums for this option using the CURLFTP_CREATE_DIR prefix. */ - CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + CURLOPT(CURLOPT_FTP_CREATE_MISSING_DIRS, CURLOPTTYPE_LONG, 110), /* Set this to a bitmask value to enable the particular authentications methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. Note that setting multiple bits may cause extra network round-trips. */ - CINIT(PROXYAUTH, LONG, 111), + CURLOPT(CURLOPT_PROXYAUTH, CURLOPTTYPE_LONG, 111), /* FTP option that changes the timeout, in seconds, associated with getting a response. This is different from transfer timeout time and essentially places a demand on the FTP server to acknowledge commands in a timely manner. */ - CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112), + CURLOPT(CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPTTYPE_LONG, 112), #define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to tell libcurl to resolve names to those IP versions only. This only has affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ - CINIT(IPRESOLVE, LONG, 113), + CURLOPT(CURLOPT_IPRESOLVE, CURLOPTTYPE_LONG, 113), /* Set this option to limit the size of a file that will be downloaded from an HTTP or FTP server. Note there is also _LARGE version which adds large file support for platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ - CINIT(MAXFILESIZE, LONG, 114), + CURLOPT(CURLOPT_MAXFILESIZE, CURLOPTTYPE_LONG, 114), /* See the comment for INFILESIZE above, but in short, specifies * the size of the file being uploaded. -1 means unknown. */ - CINIT(INFILESIZE_LARGE, OFF_T, 115), + CURLOPT(CURLOPT_INFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 115), - /* Sets the continuation offset. There is also a LONG version of this; - * look above for RESUME_FROM. + /* Sets the continuation offset. There is also a CURLOPTTYPE_LONG version + * of this; look above for RESUME_FROM. */ - CINIT(RESUME_FROM_LARGE, OFF_T, 116), + CURLOPT(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116), /* Sets the maximum size of data that will be downloaded from * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. */ - CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + CURLOPT(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117), /* Set this option to the file name of your .netrc file you want libcurl to parse (using the CURLOPT_NETRC option). If not set, libcurl will do a poor attempt to find the user's home directory and check for a .netrc file in there. */ - CINIT(NETRC_FILE, STRINGPOINT, 118), + CURLOPT(CURLOPT_NETRC_FILE, CURLOPTTYPE_STRINGPOINT, 118), /* Enable SSL/TLS for FTP, pick one of: CURLUSESSL_TRY - try using SSL, proceed anyway otherwise CURLUSESSL_CONTROL - SSL for the control connection or fail CURLUSESSL_ALL - SSL for all communication or fail */ - CINIT(USE_SSL, LONG, 119), + CURLOPT(CURLOPT_USE_SSL, CURLOPTTYPE_LONG, 119), /* The _LARGE version of the standard POSTFIELDSIZE option */ - CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + CURLOPT(CURLOPT_POSTFIELDSIZE_LARGE, CURLOPTTYPE_OFF_T, 120), /* Enable/disable the TCP Nagle algorithm */ - CINIT(TCP_NODELAY, LONG, 121), + CURLOPT(CURLOPT_TCP_NODELAY, CURLOPTTYPE_LONG, 121), /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ /* 123 OBSOLETE. Gone in 7.16.0 */ @@ -1405,143 +1438,143 @@ typedef enum { CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL */ - CINIT(FTPSSLAUTH, LONG, 129), + CURLOPT(CURLOPT_FTPSSLAUTH, CURLOPTTYPE_LONG, 129), - CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), - CINIT(IOCTLDATA, OBJECTPOINT, 131), + CURLOPT(CURLOPT_IOCTLFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 130), + CURLOPT(CURLOPT_IOCTLDATA, CURLOPTTYPE_OBJECTPOINT, 131), /* 132 OBSOLETE. Gone in 7.16.0 */ /* 133 OBSOLETE. Gone in 7.16.0 */ - /* zero terminated string for pass on to the FTP server when asked for + /* null-terminated string for pass on to the FTP server when asked for "account" info */ - CINIT(FTP_ACCOUNT, STRINGPOINT, 134), + CURLOPT(CURLOPT_FTP_ACCOUNT, CURLOPTTYPE_STRINGPOINT, 134), /* feed cookie into cookie engine */ - CINIT(COOKIELIST, STRINGPOINT, 135), + CURLOPT(CURLOPT_COOKIELIST, CURLOPTTYPE_STRINGPOINT, 135), /* ignore Content-Length */ - CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + CURLOPT(CURLOPT_IGNORE_CONTENT_LENGTH, CURLOPTTYPE_LONG, 136), /* 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 control connection. */ - CINIT(FTP_SKIP_PASV_IP, LONG, 137), + CURLOPT(CURLOPT_FTP_SKIP_PASV_IP, CURLOPTTYPE_LONG, 137), /* Select "file method" to use when doing FTP, see the curl_ftpmethod above. */ - CINIT(FTP_FILEMETHOD, LONG, 138), + CURLOPT(CURLOPT_FTP_FILEMETHOD, CURLOPTTYPE_LONG, 138), /* Local port number to bind the socket to */ - CINIT(LOCALPORT, LONG, 139), + 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. */ - CINIT(LOCALPORTRANGE, LONG, 140), + CURLOPT(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140), /* no transfer, set up connection and let application use the socket by extracting it with CURLINFO_LASTSOCKET */ - CINIT(CONNECT_ONLY, LONG, 141), + CURLOPT(CURLOPT_CONNECT_ONLY, CURLOPTTYPE_LONG, 141), /* Function that will be called to convert from the network encoding (instead of using the iconv calls in libcurl) */ - CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142), + CURLOPT(CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 142), /* Function that will be called to convert to the network encoding (instead of using the iconv calls in libcurl) */ - CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143), + CURLOPT(CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 143), /* Function that will be called to convert from UTF8 (instead of using the iconv calls in libcurl) Note that this is used only for SSL certificate processing */ - CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144), + CURLOPT(CURLOPT_CONV_FROM_UTF8_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 144), /* if the connection proceeds too quickly then need to slow it down */ /* limit-rate: maximum number of bytes per second to send or receive */ - CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), - CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), + CURLOPT(CURLOPT_MAX_SEND_SPEED_LARGE, CURLOPTTYPE_OFF_T, 145), + CURLOPT(CURLOPT_MAX_RECV_SPEED_LARGE, CURLOPTTYPE_OFF_T, 146), /* Pointer to command string to send if USER/PASS fails. */ - CINIT(FTP_ALTERNATIVE_TO_USER, STRINGPOINT, 147), + CURLOPT(CURLOPT_FTP_ALTERNATIVE_TO_USER, CURLOPTTYPE_STRINGPOINT, 147), /* callback function for setting socket options */ - CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), - CINIT(SOCKOPTDATA, OBJECTPOINT, 149), + CURLOPT(CURLOPT_SOCKOPTFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 148), + CURLOPT(CURLOPT_SOCKOPTDATA, CURLOPTTYPE_OBJECTPOINT, 149), /* set to 0 to disable session ID re-use for this transfer, default is enabled (== 1) */ - CINIT(SSL_SESSIONID_CACHE, LONG, 150), + CURLOPT(CURLOPT_SSL_SESSIONID_CACHE, CURLOPTTYPE_LONG, 150), /* allowed SSH authentication methods */ - CINIT(SSH_AUTH_TYPES, LONG, 151), + CURLOPT(CURLOPT_SSH_AUTH_TYPES, CURLOPTTYPE_LONG, 151), /* Used by scp/sftp to do public/private key authentication */ - CINIT(SSH_PUBLIC_KEYFILE, STRINGPOINT, 152), - CINIT(SSH_PRIVATE_KEYFILE, STRINGPOINT, 153), + CURLOPT(CURLOPT_SSH_PUBLIC_KEYFILE, CURLOPTTYPE_STRINGPOINT, 152), + CURLOPT(CURLOPT_SSH_PRIVATE_KEYFILE, CURLOPTTYPE_STRINGPOINT, 153), /* Send CCC (Clear Command Channel) after authentication */ - CINIT(FTP_SSL_CCC, LONG, 154), + CURLOPT(CURLOPT_FTP_SSL_CCC, CURLOPTTYPE_LONG, 154), /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ - CINIT(TIMEOUT_MS, LONG, 155), - CINIT(CONNECTTIMEOUT_MS, LONG, 156), + CURLOPT(CURLOPT_TIMEOUT_MS, CURLOPTTYPE_LONG, 155), + CURLOPT(CURLOPT_CONNECTTIMEOUT_MS, CURLOPTTYPE_LONG, 156), /* set to zero to disable the libcurl's decoding and thus pass the raw body data to the application even when it is encoded/compressed */ - CINIT(HTTP_TRANSFER_DECODING, LONG, 157), - CINIT(HTTP_CONTENT_DECODING, LONG, 158), + CURLOPT(CURLOPT_HTTP_TRANSFER_DECODING, CURLOPTTYPE_LONG, 157), + CURLOPT(CURLOPT_HTTP_CONTENT_DECODING, CURLOPTTYPE_LONG, 158), /* Permission used when creating new files and directories on the remote server for protocols that support it, SFTP/SCP/FILE */ - CINIT(NEW_FILE_PERMS, LONG, 159), - CINIT(NEW_DIRECTORY_PERMS, LONG, 160), + CURLOPT(CURLOPT_NEW_FILE_PERMS, CURLOPTTYPE_LONG, 159), + CURLOPT(CURLOPT_NEW_DIRECTORY_PERMS, CURLOPTTYPE_LONG, 160), /* Set the behaviour of POST when redirecting. Values must be set to one of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ - CINIT(POSTREDIR, LONG, 161), + CURLOPT(CURLOPT_POSTREDIR, CURLOPTTYPE_LONG, 161), /* used by scp/sftp to verify the host's public key */ - CINIT(SSH_HOST_PUBLIC_KEY_MD5, STRINGPOINT, 162), + CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, CURLOPTTYPE_STRINGPOINT, 162), /* Callback function for opening socket (instead of socket(2)). Optionally, callback is able change the address or refuse to connect returning CURL_SOCKET_BAD. The callback should have type curl_opensocket_callback */ - CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), - CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + CURLOPT(CURLOPT_OPENSOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 163), + CURLOPT(CURLOPT_OPENSOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 164), /* POST volatile input fields. */ - CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + CURLOPT(CURLOPT_COPYPOSTFIELDS, CURLOPTTYPE_OBJECTPOINT, 165), /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ - CINIT(PROXY_TRANSFER_MODE, LONG, 166), + CURLOPT(CURLOPT_PROXY_TRANSFER_MODE, CURLOPTTYPE_LONG, 166), /* Callback function for seeking in the input stream */ - CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), - CINIT(SEEKDATA, OBJECTPOINT, 168), + CURLOPT(CURLOPT_SEEKFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 167), + CURLOPT(CURLOPT_SEEKDATA, CURLOPTTYPE_OBJECTPOINT, 168), /* CRL file */ - CINIT(CRLFILE, STRINGPOINT, 169), + CURLOPT(CURLOPT_CRLFILE, CURLOPTTYPE_STRINGPOINT, 169), /* Issuer certificate */ - CINIT(ISSUERCERT, STRINGPOINT, 170), + CURLOPT(CURLOPT_ISSUERCERT, CURLOPTTYPE_STRINGPOINT, 170), /* (IPv6) Address scope */ - CINIT(ADDRESS_SCOPE, LONG, 171), + CURLOPT(CURLOPT_ADDRESS_SCOPE, CURLOPTTYPE_LONG, 171), /* Collect certificate chain info and allow it to get retrievable with CURLINFO_CERTINFO after the transfer is complete. */ - CINIT(CERTINFO, LONG, 172), + CURLOPT(CURLOPT_CERTINFO, CURLOPTTYPE_LONG, 172), /* "name" and "pwd" to use when fetching. */ - CINIT(USERNAME, STRINGPOINT, 173), - CINIT(PASSWORD, STRINGPOINT, 174), + CURLOPT(CURLOPT_USERNAME, CURLOPTTYPE_STRINGPOINT, 173), + CURLOPT(CURLOPT_PASSWORD, CURLOPTTYPE_STRINGPOINT, 174), /* "name" and "pwd" to use with Proxy when fetching. */ - CINIT(PROXYUSERNAME, STRINGPOINT, 175), - CINIT(PROXYPASSWORD, STRINGPOINT, 176), + CURLOPT(CURLOPT_PROXYUSERNAME, CURLOPTTYPE_STRINGPOINT, 175), + CURLOPT(CURLOPT_PROXYPASSWORD, CURLOPTTYPE_STRINGPOINT, 176), /* Comma separated list of hostnames defining no-proxy zones. These should match both hostnames directly, and hostnames within a domain. For @@ -1550,103 +1583,103 @@ typedef enum { implementations of this, .local.com will be considered to be the same as local.com. A single * is the only valid wildcard, and effectively disables the use of proxy. */ - CINIT(NOPROXY, STRINGPOINT, 177), + CURLOPT(CURLOPT_NOPROXY, CURLOPTTYPE_STRINGPOINT, 177), /* block size for TFTP transfers */ - CINIT(TFTP_BLKSIZE, LONG, 178), + CURLOPT(CURLOPT_TFTP_BLKSIZE, CURLOPTTYPE_LONG, 178), /* Socks Service */ - CINIT(SOCKS5_GSSAPI_SERVICE, STRINGPOINT, 179), /* DEPRECATED, do not use! */ + /* DEPRECATED, do not use! */ + CURLOPT(CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOPTTYPE_STRINGPOINT, 179), /* Socks Service */ - CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), + CURLOPT(CURLOPT_SOCKS5_GSSAPI_NEC, CURLOPTTYPE_LONG, 180), /* set the bitmask for the protocols that are allowed to be used for the transfer, which thus helps the app which takes URLs from users or other external inputs and want to restrict what protocol(s) to deal with. Defaults to CURLPROTO_ALL. */ - CINIT(PROTOCOLS, LONG, 181), + CURLOPT(CURLOPT_PROTOCOLS, CURLOPTTYPE_LONG, 181), /* set the bitmask for the protocols that libcurl is allowed to follow to, as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs - to be set in both bitmasks to be allowed to get redirected to. Defaults - to all protocols except FILE and SCP. */ - CINIT(REDIR_PROTOCOLS, LONG, 182), + to be set in both bitmasks to be allowed to get redirected to. */ + CURLOPT(CURLOPT_REDIR_PROTOCOLS, CURLOPTTYPE_LONG, 182), /* set the SSH knownhost file name to use */ - CINIT(SSH_KNOWNHOSTS, STRINGPOINT, 183), + CURLOPT(CURLOPT_SSH_KNOWNHOSTS, CURLOPTTYPE_STRINGPOINT, 183), /* set the SSH host key callback, must point to a curl_sshkeycallback function */ - CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184), + CURLOPT(CURLOPT_SSH_KEYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 184), /* set the SSH host key callback custom pointer */ - CINIT(SSH_KEYDATA, OBJECTPOINT, 185), + CURLOPT(CURLOPT_SSH_KEYDATA, CURLOPTTYPE_OBJECTPOINT, 185), /* set the SMTP mail originator */ - CINIT(MAIL_FROM, STRINGPOINT, 186), + CURLOPT(CURLOPT_MAIL_FROM, CURLOPTTYPE_STRINGPOINT, 186), /* set the list of SMTP mail receiver(s) */ - CINIT(MAIL_RCPT, OBJECTPOINT, 187), + CURLOPT(CURLOPT_MAIL_RCPT, CURLOPTTYPE_SLISTPOINT, 187), /* FTP: send PRET before PASV */ - CINIT(FTP_USE_PRET, LONG, 188), + CURLOPT(CURLOPT_FTP_USE_PRET, CURLOPTTYPE_LONG, 188), /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ - CINIT(RTSP_REQUEST, LONG, 189), + CURLOPT(CURLOPT_RTSP_REQUEST, CURLOPTTYPE_LONG, 189), /* The RTSP session identifier */ - CINIT(RTSP_SESSION_ID, STRINGPOINT, 190), + CURLOPT(CURLOPT_RTSP_SESSION_ID, CURLOPTTYPE_STRINGPOINT, 190), /* The RTSP stream URI */ - CINIT(RTSP_STREAM_URI, STRINGPOINT, 191), + CURLOPT(CURLOPT_RTSP_STREAM_URI, CURLOPTTYPE_STRINGPOINT, 191), /* The Transport: header to use in RTSP requests */ - CINIT(RTSP_TRANSPORT, STRINGPOINT, 192), + CURLOPT(CURLOPT_RTSP_TRANSPORT, CURLOPTTYPE_STRINGPOINT, 192), /* Manually initialize the client RTSP CSeq for this handle */ - CINIT(RTSP_CLIENT_CSEQ, LONG, 193), + CURLOPT(CURLOPT_RTSP_CLIENT_CSEQ, CURLOPTTYPE_LONG, 193), /* Manually initialize the server RTSP CSeq for this handle */ - CINIT(RTSP_SERVER_CSEQ, LONG, 194), + CURLOPT(CURLOPT_RTSP_SERVER_CSEQ, CURLOPTTYPE_LONG, 194), /* The stream to pass to INTERLEAVEFUNCTION. */ - CINIT(INTERLEAVEDATA, OBJECTPOINT, 195), + CURLOPT(CURLOPT_INTERLEAVEDATA, CURLOPTTYPE_OBJECTPOINT, 195), /* Let the application define a custom write method for RTP data */ - CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196), + CURLOPT(CURLOPT_INTERLEAVEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 196), /* Turn on wildcard matching */ - CINIT(WILDCARDMATCH, LONG, 197), + CURLOPT(CURLOPT_WILDCARDMATCH, CURLOPTTYPE_LONG, 197), /* Directory matching callback called before downloading of an individual file (chunk) started */ - CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198), + CURLOPT(CURLOPT_CHUNK_BGN_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 198), /* Directory matching callback called after the file (chunk) was downloaded, or skipped */ - CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199), + CURLOPT(CURLOPT_CHUNK_END_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 199), /* Change match (fnmatch-like) callback for wildcard matching */ - CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200), + CURLOPT(CURLOPT_FNMATCH_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 200), /* Let the application define custom chunk data pointer */ - CINIT(CHUNK_DATA, OBJECTPOINT, 201), + CURLOPT(CURLOPT_CHUNK_DATA, CURLOPTTYPE_OBJECTPOINT, 201), /* FNMATCH_FUNCTION user pointer */ - CINIT(FNMATCH_DATA, OBJECTPOINT, 202), + CURLOPT(CURLOPT_FNMATCH_DATA, CURLOPTTYPE_OBJECTPOINT, 202), /* send linked-list of name:port:address sets */ - CINIT(RESOLVE, OBJECTPOINT, 203), + CURLOPT(CURLOPT_RESOLVE, CURLOPTTYPE_SLISTPOINT, 203), /* Set a username for authenticated TLS */ - CINIT(TLSAUTH_USERNAME, STRINGPOINT, 204), + CURLOPT(CURLOPT_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 204), /* Set a password for authenticated TLS */ - CINIT(TLSAUTH_PASSWORD, STRINGPOINT, 205), + CURLOPT(CURLOPT_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 205), /* Set authentication type for authenticated TLS */ - CINIT(TLSAUTH_TYPE, STRINGPOINT, 206), + CURLOPT(CURLOPT_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 206), /* Set to 1 to enable the "TE:" header in HTTP requests to ask for compressed transfer-encoded responses. Set to 0 to disable the use of TE: @@ -1658,268 +1691,285 @@ typedef enum { option is set to 1. */ - CINIT(TRANSFER_ENCODING, LONG, 207), + CURLOPT(CURLOPT_TRANSFER_ENCODING, CURLOPTTYPE_LONG, 207), /* Callback function for closing socket (instead of close(2)). The callback should have type curl_closesocket_callback */ - CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208), - CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209), + CURLOPT(CURLOPT_CLOSESOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 208), + CURLOPT(CURLOPT_CLOSESOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 209), /* allow GSSAPI credential delegation */ - CINIT(GSSAPI_DELEGATION, LONG, 210), + CURLOPT(CURLOPT_GSSAPI_DELEGATION, CURLOPTTYPE_LONG, 210), /* Set the name servers to use for DNS resolution */ - CINIT(DNS_SERVERS, STRINGPOINT, 211), + CURLOPT(CURLOPT_DNS_SERVERS, CURLOPTTYPE_STRINGPOINT, 211), /* Time-out accept operations (currently for FTP only) after this amount of milliseconds. */ - CINIT(ACCEPTTIMEOUT_MS, LONG, 212), + CURLOPT(CURLOPT_ACCEPTTIMEOUT_MS, CURLOPTTYPE_LONG, 212), /* Set TCP keepalive */ - CINIT(TCP_KEEPALIVE, LONG, 213), + CURLOPT(CURLOPT_TCP_KEEPALIVE, CURLOPTTYPE_LONG, 213), /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */ - CINIT(TCP_KEEPIDLE, LONG, 214), - CINIT(TCP_KEEPINTVL, LONG, 215), + CURLOPT(CURLOPT_TCP_KEEPIDLE, CURLOPTTYPE_LONG, 214), + CURLOPT(CURLOPT_TCP_KEEPINTVL, CURLOPTTYPE_LONG, 215), /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */ - CINIT(SSL_OPTIONS, LONG, 216), + CURLOPT(CURLOPT_SSL_OPTIONS, CURLOPTTYPE_LONG, 216), /* Set the SMTP auth originator */ - CINIT(MAIL_AUTH, STRINGPOINT, 217), + CURLOPT(CURLOPT_MAIL_AUTH, CURLOPTTYPE_STRINGPOINT, 217), /* Enable/disable SASL initial response */ - CINIT(SASL_IR, LONG, 218), + CURLOPT(CURLOPT_SASL_IR, CURLOPTTYPE_LONG, 218), /* Function that will be called instead of the internal progress display * function. This function should be defined as the curl_xferinfo_callback * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ - CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219), + CURLOPT(CURLOPT_XFERINFOFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 219), /* The XOAUTH2 bearer token */ - CINIT(XOAUTH2_BEARER, STRINGPOINT, 220), + CURLOPT(CURLOPT_XOAUTH2_BEARER, CURLOPTTYPE_STRINGPOINT, 220), /* Set the interface string to use as outgoing network * interface for DNS requests. * Only supported by the c-ares DNS backend */ - CINIT(DNS_INTERFACE, STRINGPOINT, 221), + CURLOPT(CURLOPT_DNS_INTERFACE, CURLOPTTYPE_STRINGPOINT, 221), /* Set the local IPv4 address to use for outgoing DNS requests. * Only supported by the c-ares DNS backend */ - CINIT(DNS_LOCAL_IP4, STRINGPOINT, 222), + CURLOPT(CURLOPT_DNS_LOCAL_IP4, CURLOPTTYPE_STRINGPOINT, 222), /* Set the local IPv6 address to use for outgoing DNS requests. * Only supported by the c-ares DNS backend */ - CINIT(DNS_LOCAL_IP6, STRINGPOINT, 223), + CURLOPT(CURLOPT_DNS_LOCAL_IP6, CURLOPTTYPE_STRINGPOINT, 223), /* Set authentication options directly */ - CINIT(LOGIN_OPTIONS, STRINGPOINT, 224), + CURLOPT(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_STRINGPOINT, 224), /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ - CINIT(SSL_ENABLE_NPN, LONG, 225), + CURLOPT(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225), /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ - CINIT(SSL_ENABLE_ALPN, LONG, 226), + CURLOPT(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226), /* Time to wait for a response to a HTTP request containing an * Expect: 100-continue header before sending the data anyway. */ - CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227), + CURLOPT(CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOPTTYPE_LONG, 227), /* This points to a linked list of headers used for proxy requests only, struct curl_slist kind */ - CINIT(PROXYHEADER, OBJECTPOINT, 228), + CURLOPT(CURLOPT_PROXYHEADER, CURLOPTTYPE_SLISTPOINT, 228), /* Pass in a bitmask of "header options" */ - CINIT(HEADEROPT, LONG, 229), + CURLOPT(CURLOPT_HEADEROPT, CURLOPTTYPE_LONG, 229), /* The public key in DER form used to validate the peer public key this option is used only if SSL_VERIFYPEER is true */ - CINIT(PINNEDPUBLICKEY, STRINGPOINT, 230), + CURLOPT(CURLOPT_PINNEDPUBLICKEY, CURLOPTTYPE_STRINGPOINT, 230), /* Path to Unix domain socket */ - CINIT(UNIX_SOCKET_PATH, STRINGPOINT, 231), + CURLOPT(CURLOPT_UNIX_SOCKET_PATH, CURLOPTTYPE_STRINGPOINT, 231), /* Set if we should verify the certificate status. */ - CINIT(SSL_VERIFYSTATUS, LONG, 232), + CURLOPT(CURLOPT_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 232), /* Set if we should enable TLS false start. */ - CINIT(SSL_FALSESTART, LONG, 233), + CURLOPT(CURLOPT_SSL_FALSESTART, CURLOPTTYPE_LONG, 233), /* Do not squash dot-dot sequences */ - CINIT(PATH_AS_IS, LONG, 234), + CURLOPT(CURLOPT_PATH_AS_IS, CURLOPTTYPE_LONG, 234), /* Proxy Service Name */ - CINIT(PROXY_SERVICE_NAME, STRINGPOINT, 235), + CURLOPT(CURLOPT_PROXY_SERVICE_NAME, CURLOPTTYPE_STRINGPOINT, 235), /* Service Name */ - CINIT(SERVICE_NAME, STRINGPOINT, 236), + CURLOPT(CURLOPT_SERVICE_NAME, CURLOPTTYPE_STRINGPOINT, 236), /* Wait/don't wait for pipe/mutex to clarify */ - CINIT(PIPEWAIT, LONG, 237), + CURLOPT(CURLOPT_PIPEWAIT, CURLOPTTYPE_LONG, 237), /* Set the protocol used when curl is given a URL without a protocol */ - CINIT(DEFAULT_PROTOCOL, STRINGPOINT, 238), + CURLOPT(CURLOPT_DEFAULT_PROTOCOL, CURLOPTTYPE_STRINGPOINT, 238), /* Set stream weight, 1 - 256 (default is 16) */ - CINIT(STREAM_WEIGHT, LONG, 239), + CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239), /* Set stream dependency on another CURL handle */ - CINIT(STREAM_DEPENDS, OBJECTPOINT, 240), + CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240), /* Set E-xclusive stream dependency on another CURL handle */ - CINIT(STREAM_DEPENDS_E, OBJECTPOINT, 241), + CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241), /* Do not send any tftp option requests to the server */ - CINIT(TFTP_NO_OPTIONS, LONG, 242), + CURLOPT(CURLOPT_TFTP_NO_OPTIONS, CURLOPTTYPE_LONG, 242), /* Linked-list of host:port:connect-to-host:connect-to-port, overrides the URL's host:port (only for the network layer) */ - CINIT(CONNECT_TO, OBJECTPOINT, 243), + CURLOPT(CURLOPT_CONNECT_TO, CURLOPTTYPE_SLISTPOINT, 243), /* Set TCP Fast Open */ - CINIT(TCP_FASTOPEN, LONG, 244), + CURLOPT(CURLOPT_TCP_FASTOPEN, CURLOPTTYPE_LONG, 244), /* Continue to send data if the server responds early with an * HTTP status code >= 300 */ - CINIT(KEEP_SENDING_ON_ERROR, LONG, 245), + CURLOPT(CURLOPT_KEEP_SENDING_ON_ERROR, CURLOPTTYPE_LONG, 245), /* The CApath or CAfile used to validate the proxy certificate this option is used only if PROXY_SSL_VERIFYPEER is true */ - CINIT(PROXY_CAINFO, STRINGPOINT, 246), + CURLOPT(CURLOPT_PROXY_CAINFO, CURLOPTTYPE_STRINGPOINT, 246), /* The CApath directory used to validate the proxy certificate this option is used only if PROXY_SSL_VERIFYPEER is true */ - CINIT(PROXY_CAPATH, STRINGPOINT, 247), + CURLOPT(CURLOPT_PROXY_CAPATH, CURLOPTTYPE_STRINGPOINT, 247), /* Set if we should verify the proxy in ssl handshake, set 1 to verify. */ - CINIT(PROXY_SSL_VERIFYPEER, LONG, 248), + CURLOPT(CURLOPT_PROXY_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 248), /* Set if we should verify the Common name from the proxy certificate in ssl * handshake, set 1 to check existence, 2 to ensure that it matches * the provided hostname. */ - CINIT(PROXY_SSL_VERIFYHOST, LONG, 249), + CURLOPT(CURLOPT_PROXY_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 249), /* What version to specifically try to use for proxy. See CURL_SSLVERSION defines below. */ - CINIT(PROXY_SSLVERSION, LONG, 250), + CURLOPT(CURLOPT_PROXY_SSLVERSION, CURLOPTTYPE_LONG, 250), /* Set a username for authenticated TLS for proxy */ - CINIT(PROXY_TLSAUTH_USERNAME, STRINGPOINT, 251), + CURLOPT(CURLOPT_PROXY_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 251), /* Set a password for authenticated TLS for proxy */ - CINIT(PROXY_TLSAUTH_PASSWORD, STRINGPOINT, 252), + CURLOPT(CURLOPT_PROXY_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 252), /* Set authentication type for authenticated TLS for proxy */ - CINIT(PROXY_TLSAUTH_TYPE, STRINGPOINT, 253), + CURLOPT(CURLOPT_PROXY_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 253), /* name of the file keeping your private SSL-certificate for proxy */ - CINIT(PROXY_SSLCERT, STRINGPOINT, 254), + CURLOPT(CURLOPT_PROXY_SSLCERT, CURLOPTTYPE_STRINGPOINT, 254), /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") for proxy */ - CINIT(PROXY_SSLCERTTYPE, STRINGPOINT, 255), + CURLOPT(CURLOPT_PROXY_SSLCERTTYPE, CURLOPTTYPE_STRINGPOINT, 255), /* name of the file keeping your private SSL-key for proxy */ - CINIT(PROXY_SSLKEY, STRINGPOINT, 256), + CURLOPT(CURLOPT_PROXY_SSLKEY, CURLOPTTYPE_STRINGPOINT, 256), /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") for proxy */ - CINIT(PROXY_SSLKEYTYPE, STRINGPOINT, 257), + CURLOPT(CURLOPT_PROXY_SSLKEYTYPE, CURLOPTTYPE_STRINGPOINT, 257), /* password for the SSL private key for proxy */ - CINIT(PROXY_KEYPASSWD, STRINGPOINT, 258), + CURLOPT(CURLOPT_PROXY_KEYPASSWD, CURLOPTTYPE_STRINGPOINT, 258), /* Specify which SSL ciphers to use for proxy */ - CINIT(PROXY_SSL_CIPHER_LIST, STRINGPOINT, 259), + CURLOPT(CURLOPT_PROXY_SSL_CIPHER_LIST, CURLOPTTYPE_STRINGPOINT, 259), /* CRL file for proxy */ - CINIT(PROXY_CRLFILE, STRINGPOINT, 260), + CURLOPT(CURLOPT_PROXY_CRLFILE, CURLOPTTYPE_STRINGPOINT, 260), /* Enable/disable specific SSL features with a bitmask for proxy, see CURLSSLOPT_* */ - CINIT(PROXY_SSL_OPTIONS, LONG, 261), + CURLOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLOPTTYPE_LONG, 261), /* Name of pre proxy to use. */ - CINIT(PRE_PROXY, STRINGPOINT, 262), + CURLOPT(CURLOPT_PRE_PROXY, CURLOPTTYPE_STRINGPOINT, 262), /* The public key in DER form used to validate the proxy public key this option is used only if PROXY_SSL_VERIFYPEER is true */ - CINIT(PROXY_PINNEDPUBLICKEY, STRINGPOINT, 263), + CURLOPT(CURLOPT_PROXY_PINNEDPUBLICKEY, CURLOPTTYPE_STRINGPOINT, 263), /* Path to an abstract Unix domain socket */ - CINIT(ABSTRACT_UNIX_SOCKET, STRINGPOINT, 264), + CURLOPT(CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOPTTYPE_STRINGPOINT, 264), /* Suppress proxy CONNECT response headers from user callbacks */ - CINIT(SUPPRESS_CONNECT_HEADERS, LONG, 265), + CURLOPT(CURLOPT_SUPPRESS_CONNECT_HEADERS, CURLOPTTYPE_LONG, 265), /* The request target, instead of extracted from the URL */ - CINIT(REQUEST_TARGET, STRINGPOINT, 266), + CURLOPT(CURLOPT_REQUEST_TARGET, CURLOPTTYPE_STRINGPOINT, 266), /* bitmask of allowed auth methods for connections to SOCKS5 proxies */ - CINIT(SOCKS5_AUTH, LONG, 267), + CURLOPT(CURLOPT_SOCKS5_AUTH, CURLOPTTYPE_LONG, 267), /* Enable/disable SSH compression */ - CINIT(SSH_COMPRESSION, LONG, 268), + CURLOPT(CURLOPT_SSH_COMPRESSION, CURLOPTTYPE_LONG, 268), /* Post MIME data. */ - CINIT(MIMEPOST, OBJECTPOINT, 269), + CURLOPT(CURLOPT_MIMEPOST, CURLOPTTYPE_OBJECTPOINT, 269), /* Time to use with the CURLOPT_TIMECONDITION. Specified in number of seconds since 1 Jan 1970. */ - CINIT(TIMEVALUE_LARGE, OFF_T, 270), + CURLOPT(CURLOPT_TIMEVALUE_LARGE, CURLOPTTYPE_OFF_T, 270), /* Head start in milliseconds to give happy eyeballs. */ - CINIT(HAPPY_EYEBALLS_TIMEOUT_MS, LONG, 271), + CURLOPT(CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOPTTYPE_LONG, 271), /* Function that will be called before a resolver request is made */ - CINIT(RESOLVER_START_FUNCTION, FUNCTIONPOINT, 272), + CURLOPT(CURLOPT_RESOLVER_START_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 272), /* User data to pass to the resolver start callback. */ - CINIT(RESOLVER_START_DATA, OBJECTPOINT, 273), + CURLOPT(CURLOPT_RESOLVER_START_DATA, CURLOPTTYPE_OBJECTPOINT, 273), /* send HAProxy PROXY protocol header? */ - CINIT(HAPROXYPROTOCOL, LONG, 274), + CURLOPT(CURLOPT_HAPROXYPROTOCOL, CURLOPTTYPE_LONG, 274), /* shuffle addresses before use when DNS returns multiple */ - CINIT(DNS_SHUFFLE_ADDRESSES, LONG, 275), + CURLOPT(CURLOPT_DNS_SHUFFLE_ADDRESSES, CURLOPTTYPE_LONG, 275), /* Specify which TLS 1.3 ciphers suites to use */ - CINIT(TLS13_CIPHERS, STRINGPOINT, 276), - CINIT(PROXY_TLS13_CIPHERS, STRINGPOINT, 277), + CURLOPT(CURLOPT_TLS13_CIPHERS, CURLOPTTYPE_STRINGPOINT, 276), + CURLOPT(CURLOPT_PROXY_TLS13_CIPHERS, CURLOPTTYPE_STRINGPOINT, 277), /* Disallow specifying username/login in URL. */ - CINIT(DISALLOW_USERNAME_IN_URL, LONG, 278), + CURLOPT(CURLOPT_DISALLOW_USERNAME_IN_URL, CURLOPTTYPE_LONG, 278), /* DNS-over-HTTPS URL */ - CINIT(DOH_URL, STRINGPOINT, 279), + CURLOPT(CURLOPT_DOH_URL, CURLOPTTYPE_STRINGPOINT, 279), /* Preferred buffer size to use for uploads */ - CINIT(UPLOAD_BUFFERSIZE, LONG, 280), + CURLOPT(CURLOPT_UPLOAD_BUFFERSIZE, CURLOPTTYPE_LONG, 280), /* Time in ms between connection upkeep calls for long-lived connections. */ - CINIT(UPKEEP_INTERVAL_MS, LONG, 281), + CURLOPT(CURLOPT_UPKEEP_INTERVAL_MS, CURLOPTTYPE_LONG, 281), /* Specify URL using CURL URL API. */ - CINIT(CURLU, OBJECTPOINT, 282), + CURLOPT(CURLOPT_CURLU, CURLOPTTYPE_OBJECTPOINT, 282), /* add trailing data just after no more data is available */ - CINIT(TRAILERFUNCTION, FUNCTIONPOINT, 283), + CURLOPT(CURLOPT_TRAILERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 283), /* pointer to be passed to HTTP_TRAILER_FUNCTION */ - CINIT(TRAILERDATA, OBJECTPOINT, 284), + CURLOPT(CURLOPT_TRAILERDATA, CURLOPTTYPE_OBJECTPOINT, 284), /* set this to 1L to allow HTTP/0.9 responses or 0L to disallow */ - CINIT(HTTP09_ALLOWED, LONG, 285), + CURLOPT(CURLOPT_HTTP09_ALLOWED, CURLOPTTYPE_LONG, 285), /* alt-svc control bitmask */ - CINIT(ALTSVC_CTRL, LONG, 286), + CURLOPT(CURLOPT_ALTSVC_CTRL, CURLOPTTYPE_LONG, 286), /* alt-svc cache file name to possibly read from/write to */ - CINIT(ALTSVC, STRINGPOINT, 287), + CURLOPT(CURLOPT_ALTSVC, CURLOPTTYPE_STRINGPOINT, 287), /* maximum age of a connection to consider it for reuse (in seconds) */ - CINIT(MAXAGE_CONN, LONG, 288), + CURLOPT(CURLOPT_MAXAGE_CONN, CURLOPTTYPE_LONG, 288), + + /* SASL authorisation identity */ + CURLOPT(CURLOPT_SASL_AUTHZID, CURLOPTTYPE_STRINGPOINT, 289), + + /* allow RCPT TO command to fail for some recipients */ + CURLOPT(CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOPTTYPE_LONG, 290), + + /* the private SSL-certificate as a "blob" */ + CURLOPT(CURLOPT_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 291), + CURLOPT(CURLOPT_SSLKEY_BLOB, CURLOPTTYPE_BLOB, 292), + CURLOPT(CURLOPT_PROXY_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 293), + CURLOPT(CURLOPT_PROXY_SSLKEY_BLOB, CURLOPTTYPE_BLOB, 294), + CURLOPT(CURLOPT_ISSUERCERT_BLOB, CURLOPTTYPE_BLOB, 295), + + /* Issuer certificate for proxy */ + CURLOPT(CURLOPT_PROXY_ISSUERCERT, CURLOPTTYPE_STRINGPOINT, 296), + CURLOPT(CURLOPT_PROXY_ISSUERCERT_BLOB, CURLOPTTYPE_BLOB, 297), CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -1974,7 +2024,8 @@ enum { CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */ CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1 Upgrade */ - + CURL_HTTP_VERSION_3 = 30, /* Makes use of explicit HTTP/3 without fallback. + Use CURLOPT_ALTSVC to enable HTTP/3 upgrade */ CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ }; @@ -2067,7 +2118,7 @@ typedef enum { CURL_TIMECOND_LAST } curl_TimeCond; -/* Special size_t value signaling a zero-terminated string. */ +/* Special size_t value signaling a null-terminated string. */ #define CURL_ZERO_TERMINATED ((size_t) -1) /* curl_strequal() and curl_strnequal() are subject for removal in a future @@ -2076,8 +2127,8 @@ CURL_EXTERN int curl_strequal(const char *s1, const char *s2); CURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n); /* Mime/form handling support. */ -typedef struct curl_mime_s curl_mime; /* Mime context. */ -typedef struct curl_mimepart_s curl_mimepart; /* Mime part context. */ +typedef struct curl_mime curl_mime; /* Mime context. */ +typedef struct curl_mimepart curl_mimepart; /* Mime part context. */ /* * NAME curl_mime_init() @@ -2200,52 +2251,35 @@ CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part, struct curl_slist *headers, int take_ownership); -/* Old form API. */ -/* name is uppercase CURLFORM_ */ -#ifdef CFINIT -#undef CFINIT -#endif - -#ifdef CURL_ISOCPP -#define CFINIT(name) CURLFORM_ ## name -#else -/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ -#define CFINIT(name) CURLFORM_/**/name -#endif - typedef enum { - CFINIT(NOTHING), /********* the first one is unused ************/ - - /* */ - CFINIT(COPYNAME), - CFINIT(PTRNAME), - CFINIT(NAMELENGTH), - CFINIT(COPYCONTENTS), - CFINIT(PTRCONTENTS), - CFINIT(CONTENTSLENGTH), - CFINIT(FILECONTENT), - CFINIT(ARRAY), - CFINIT(OBSOLETE), - CFINIT(FILE), - - CFINIT(BUFFER), - CFINIT(BUFFERPTR), - CFINIT(BUFFERLENGTH), - - CFINIT(CONTENTTYPE), - CFINIT(CONTENTHEADER), - CFINIT(FILENAME), - CFINIT(END), - CFINIT(OBSOLETE2), - - CFINIT(STREAM), - CFINIT(CONTENTLEN), /* added in 7.46.0, provide a curl_off_t length */ + CURLFORM_NOTHING, /********* the first one is unused ************/ + CURLFORM_COPYNAME, + CURLFORM_PTRNAME, + CURLFORM_NAMELENGTH, + CURLFORM_COPYCONTENTS, + CURLFORM_PTRCONTENTS, + CURLFORM_CONTENTSLENGTH, + CURLFORM_FILECONTENT, + CURLFORM_ARRAY, + CURLFORM_OBSOLETE, + CURLFORM_FILE, + + CURLFORM_BUFFER, + CURLFORM_BUFFERPTR, + CURLFORM_BUFFERLENGTH, + + CURLFORM_CONTENTTYPE, + CURLFORM_CONTENTHEADER, + CURLFORM_FILENAME, + CURLFORM_END, + CURLFORM_OBSOLETE2, + + CURLFORM_STREAM, + CURLFORM_CONTENTLEN, /* added in 7.46.0, provide a curl_off_t length */ CURLFORM_LASTENTRY /* the last unused */ } CURLformoption; -#undef CFINIT /* done */ - /* structure to be used as parameter for CURLFORM_ARRAY */ struct curl_forms { CURLformoption option; @@ -2416,7 +2450,7 @@ CURL_EXTERN CURLcode curl_global_init(long flags); * initialize libcurl and set user defined memory management callback * functions. Users can implement memory management routines to check for * memory leaks, check for mis-use of the curl library etc. User registered - * callback routines with be invoked by this library instead of the system + * callback routines will be invoked by this library instead of the system * memory management routines like malloc, free etc. */ CURL_EXTERN CURLcode curl_global_init_mem(long flags, @@ -2468,10 +2502,11 @@ struct curl_slist { * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE. */ -typedef struct { +struct curl_ssl_backend { curl_sslbackend id; const char *name; -} curl_ssl_backend; +}; +typedef struct curl_ssl_backend curl_ssl_backend; typedef enum { CURLSSLSET_OK = 0, @@ -2514,8 +2549,8 @@ CURL_EXTERN void curl_slist_free_all(struct curl_slist *); */ CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); -/* info about the certificate chain, only for OpenSSL builds. Asked - for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +/* info about the certificate chain, only for OpenSSL, GnuTLS, Schannel, NSS + and GSKit builds. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ struct curl_certinfo { int num_of_certs; /* number of certificates with information */ struct curl_slist **certinfo; /* for each index in this array, there's a @@ -2610,8 +2645,9 @@ typedef enum { CURLINFO_STARTTRANSFER_TIME_T = CURLINFO_OFF_T + 54, CURLINFO_REDIRECT_TIME_T = CURLINFO_OFF_T + 55, CURLINFO_APPCONNECT_TIME_T = CURLINFO_OFF_T + 56, + CURLINFO_RETRY_AFTER = CURLINFO_OFF_T + 57, - CURLINFO_LASTONE = 56 + CURLINFO_LASTONE = 57 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -2710,6 +2746,8 @@ typedef enum { CURLVERSION_THIRD, CURLVERSION_FOURTH, CURLVERSION_FIFTH, + CURLVERSION_SIXTH, + CURLVERSION_SEVENTH, CURLVERSION_LAST /* never actually use this */ } CURLversion; @@ -2718,9 +2756,9 @@ typedef enum { meant to be a built-in version number for what kind of struct the caller expects. If the struct ever changes, we redefine the NOW to another enum from above. */ -#define CURLVERSION_NOW CURLVERSION_FIFTH +#define CURLVERSION_NOW CURLVERSION_SEVENTH -typedef struct { +struct curl_version_info_data { CURLversion age; /* age of the returned struct */ const char *version; /* LIBCURL_VERSION */ unsigned int version_num; /* LIBCURL_VERSION_NUM */ @@ -2747,12 +2785,25 @@ typedef struct { const char *libssh_version; /* human readable string */ /* These fields were added in CURLVERSION_FIFTH */ - unsigned int brotli_ver_num; /* Numeric Brotli version (MAJOR << 24) | (MINOR << 12) | PATCH */ const char *brotli_version; /* human readable string. */ -} curl_version_info_data; + /* These fields were added in CURLVERSION_SIXTH */ + unsigned int nghttp2_ver_num; /* Numeric nghttp2 version + (MAJOR << 16) | (MINOR << 8) | PATCH */ + const char *nghttp2_version; /* human readable string. */ + const char *quic_version; /* human readable quic (+ HTTP/3) library + + version or NULL */ + + /* These fields were added in CURLVERSION_SEVENTH */ + const char *cainfo; /* the built-in default CURLOPT_CAINFO, might + be NULL */ + const char *capath; /* the built-in default CURLOPT_CAPATH, might + be NULL */ + +}; +typedef struct curl_version_info_data curl_version_info_data; #define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ #define CURL_VERSION_KERBEROS4 (1<<1) /* Kerberos V4 auth is supported @@ -2784,6 +2835,7 @@ typedef struct { #define CURL_VERSION_MULTI_SSL (1<<22) /* Multiple SSL backends available */ #define CURL_VERSION_BROTLI (1<<23) /* Brotli features are present. */ #define CURL_VERSION_ALTSVC (1<<24) /* Alt-Svc handling built-in */ +#define CURL_VERSION_HTTP3 (1<<25) /* HTTP3 support built-in */ /* * NAME curl_version_info() @@ -2866,4 +2918,4 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); #endif /* __STDC__ >= 1 */ #endif /* gcc >= 4.3 && !__cplusplus */ -#endif /* __CURL_CURL_H */ +#endif /* CURLINC_CURL_H */ diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index 04efb93ae0b..97a8bd87697 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h @@ -1,5 +1,5 @@ -#ifndef __CURL_CURLVER_H -#define __CURL_CURLVER_H +#ifndef CURLINC_CURLVER_H +#define CURLINC_CURLVER_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,20 +26,20 @@ a script at release-time. This was made its own header file in 7.11.2 */ /* This is the global package copyright */ -#define LIBCURL_COPYRIGHT "1996 - 2019 Daniel Stenberg, ." +#define LIBCURL_COPYRIGHT "1996 - 2020 Daniel Stenberg, ." /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.65.0" +#define LIBCURL_VERSION "7.71.1" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 65 -#define LIBCURL_VERSION_PATCH 0 +#define LIBCURL_VERSION_MINOR 71 +#define LIBCURL_VERSION_PATCH 1 /* This is the numeric version of the libcurl version number, meant for easier - parsing and comparions by programs. The LIBCURL_VERSION_NUM define will + parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will always follow this syntax: 0xXXYYZZ @@ -57,7 +57,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 0x074100 +#define LIBCURL_VERSION_NUM 0x074701 /* * This is the date and time when the full source package was created. The @@ -74,4 +74,4 @@ #define CURL_AT_LEAST_VERSION(x,y,z) \ (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) -#endif /* __CURL_CURLVER_H */ +#endif /* CURLINC_CURLVER_H */ diff --git a/Utilities/cmcurl/include/curl/easy.h b/Utilities/cmcurl/include/curl/easy.h index f42a8a96927..9aef1339620 100644 --- a/Utilities/cmcurl/include/curl/easy.h +++ b/Utilities/cmcurl/include/curl/easy.h @@ -1,5 +1,5 @@ -#ifndef __CURL_EASY_H -#define __CURL_EASY_H +#ifndef CURLINC_EASY_H +#define CURLINC_EASY_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,6 +25,17 @@ extern "C" { #endif +/* Flag bits in the curl_blob struct: */ +#define CURL_BLOB_COPY 1 /* tell libcurl to copy the data */ +#define CURL_BLOB_NOCOPY 0 /* tell libcurl to NOT copy the data */ + +struct curl_blob { + void *data; + size_t len; + unsigned int flags; /* bit 0 is defined, the rest are reserved and should be + left zeroes */ +}; + CURL_EXTERN CURL *curl_easy_init(void); CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); diff --git a/Utilities/cmcurl/include/curl/mprintf.h b/Utilities/cmcurl/include/curl/mprintf.h index e20f546e199..f615ed7d63d 100644 --- a/Utilities/cmcurl/include/curl/mprintf.h +++ b/Utilities/cmcurl/include/curl/mprintf.h @@ -1,5 +1,5 @@ -#ifndef __CURL_MPRINTF_H -#define __CURL_MPRINTF_H +#ifndef CURLINC_MPRINTF_H +#define CURLINC_MPRINTF_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -47,4 +47,4 @@ CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); } #endif -#endif /* __CURL_MPRINTF_H */ +#endif /* CURLINC_MPRINTF_H */ diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h index b19dbaf7911..2e6bb72d648 100644 --- a/Utilities/cmcurl/include/curl/multi.h +++ b/Utilities/cmcurl/include/curl/multi.h @@ -1,5 +1,5 @@ -#ifndef __CURL_MULTI_H -#define __CURL_MULTI_H +#ifndef CURLINC_MULTI_H +#define CURLINC_MULTI_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -72,6 +72,8 @@ typedef enum { attempted to get added - again */ CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a callback */ + CURLM_WAKEUP_FAILURE, /* wakeup is unavailable or failed */ + CURLM_BAD_FUNCTION_ARGUMENT, /* function called with a bad parameter */ CURLM_LAST } CURLMcode; @@ -173,6 +175,29 @@ CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, int timeout_ms, int *ret); +/* + * Name: curl_multi_poll() + * + * Desc: Poll on all fds within a CURLM set as well as any + * additional fds passed to the function. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret); + +/* + * Name: curl_multi_wakeup() + * + * Desc: wakes up a sleeping curl_multi_poll call. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle); + /* * Name: curl_multi_perform() * @@ -242,7 +267,7 @@ CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, * value into the equivalent human readable error string. This is * useful for printing meaningful error messages. * - * Returns: A pointer to a zero-terminated error message. + * Returns: A pointer to a null-terminated error message. */ CURL_EXTERN const char *curl_multi_strerror(CURLMcode); @@ -319,68 +344,56 @@ CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, long *milliseconds); -#undef CINIT /* re-using the same name as in curl.h */ - -#ifdef CURL_ISOCPP -#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num -#else -/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ -#define LONG CURLOPTTYPE_LONG -#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT -#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT -#define OFF_T CURLOPTTYPE_OFF_T -#define CINIT(name,type,number) CURLMOPT_/**/name = type + number -#endif - typedef enum { /* This is the socket callback function pointer */ - CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1), + CURLOPT(CURLMOPT_SOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 1), /* This is the argument passed to the socket callback */ - CINIT(SOCKETDATA, OBJECTPOINT, 2), + CURLOPT(CURLMOPT_SOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 2), /* set to 1 to enable pipelining for this multi handle */ - CINIT(PIPELINING, LONG, 3), + CURLOPT(CURLMOPT_PIPELINING, CURLOPTTYPE_LONG, 3), /* This is the timer callback function pointer */ - CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4), + CURLOPT(CURLMOPT_TIMERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 4), /* This is the argument passed to the timer callback */ - CINIT(TIMERDATA, OBJECTPOINT, 5), + CURLOPT(CURLMOPT_TIMERDATA, CURLOPTTYPE_OBJECTPOINT, 5), /* maximum number of entries in the connection cache */ - CINIT(MAXCONNECTS, LONG, 6), + CURLOPT(CURLMOPT_MAXCONNECTS, CURLOPTTYPE_LONG, 6), /* maximum number of (pipelining) connections to one host */ - CINIT(MAX_HOST_CONNECTIONS, LONG, 7), + CURLOPT(CURLMOPT_MAX_HOST_CONNECTIONS, CURLOPTTYPE_LONG, 7), /* maximum number of requests in a pipeline */ - CINIT(MAX_PIPELINE_LENGTH, LONG, 8), + CURLOPT(CURLMOPT_MAX_PIPELINE_LENGTH, CURLOPTTYPE_LONG, 8), /* a connection with a content-length longer than this will not be considered for pipelining */ - CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9), + 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 */ - CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10), + CURLOPT(CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 10), - /* a list of site names(+port) that are blacklisted from - pipelining */ - CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11), + /* a list of site names(+port) that are blocked from pipelining */ + CURLOPT(CURLMOPT_PIPELINING_SITE_BL, CURLOPTTYPE_OBJECTPOINT, 11), - /* a list of server types that are blacklisted from - pipelining */ - CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12), + /* a list of server types that are blocked from pipelining */ + CURLOPT(CURLMOPT_PIPELINING_SERVER_BL, CURLOPTTYPE_OBJECTPOINT, 12), /* maximum number of open connections in total */ - CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13), + CURLOPT(CURLMOPT_MAX_TOTAL_CONNECTIONS, CURLOPTTYPE_LONG, 13), /* This is the server push callback function pointer */ - CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14), + CURLOPT(CURLMOPT_PUSHFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 14), /* This is the argument passed to the server push callback */ - CINIT(PUSHDATA, OBJECTPOINT, 15), + CURLOPT(CURLMOPT_PUSHDATA, CURLOPTTYPE_OBJECTPOINT, 15), + + /* maximum number of concurrent streams to support on a connection */ + CURLOPT(CURLMOPT_MAX_CONCURRENT_STREAMS, CURLOPTTYPE_LONG, 16), CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; diff --git a/Utilities/cmcurl/include/curl/stdcheaders.h b/Utilities/cmcurl/include/curl/stdcheaders.h index 027b6f42117..a6bdc1a25ca 100644 --- a/Utilities/cmcurl/include/curl/stdcheaders.h +++ b/Utilities/cmcurl/include/curl/stdcheaders.h @@ -1,5 +1,5 @@ -#ifndef __STDC_HEADERS_H -#define __STDC_HEADERS_H +#ifndef CURLINC_STDCHEADERS_H +#define CURLINC_STDCHEADERS_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,4 +30,4 @@ size_t fwrite(const void *, size_t, size_t, FILE *); int strcasecmp(const char *, const char *); int strncasecmp(const char *, const char *, size_t); -#endif /* __STDC_HEADERS_H */ +#endif /* CURLINC_STDCHEADERS_H */ diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h index 1e555ec19e3..867af614187 100644 --- a/Utilities/cmcurl/include/curl/system.h +++ b/Utilities/cmcurl/include/curl/system.h @@ -1,5 +1,5 @@ -#ifndef __CURL_SYSTEM_H -#define __CURL_SYSTEM_H +#ifndef CURLINC_SYSTEM_H +#define CURLINC_SYSTEM_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -137,15 +137,26 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__LCC__) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# define CURL_TYPEOF_CURL_SOCKLEN_T int +# if defined(__e2k__) /* MCST eLbrus C Compiler */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# else /* Local (or Little) C Compiler */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# endif #elif defined(__SYMBIAN32__) -# if defined(__EABI__) /* Treat all ARM compilers equally */ +# if defined(__EABI__) /* Treat all ARM compilers equally */ # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" @@ -288,7 +299,6 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__TINYC__) /* also known as tcc */ - # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" @@ -377,6 +387,7 @@ # define CURL_SUFFIX_CURL_OFF_TU ULL # elif defined(__LP64__) || \ defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \ + defined(__e2k__) || \ (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \ (defined(__LONG_MAX__) && __LONG_MAX__ == 9223372036854775807L) # define CURL_TYPEOF_CURL_OFF_T long @@ -473,21 +484,21 @@ */ #if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551) -# define __CURL_OFF_T_C_HLPR2(x) x -# define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x) -# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ - __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) -# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ - __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) +# define CURLINC_OFF_T_C_HLPR2(x) x +# define CURLINC_OFF_T_C_HLPR1(x) CURLINC_OFF_T_C_HLPR2(x) +# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ + CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ + CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) #else # ifdef CURL_ISOCPP -# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix +# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix # else -# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix +# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix # endif -# define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix) -# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) -# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) +# define CURLINC_OFF_T_C_HLPR1(Val,Suffix) CURLINC_OFF_T_C_HLPR2(Val,Suffix) +# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) #endif -#endif /* __CURL_SYSTEM_H */ +#endif /* CURLINC_SYSTEM_H */ diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h index 2d1de4d43aa..f8cb921f2ec 100644 --- a/Utilities/cmcurl/include/curl/typecheck-gcc.h +++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h @@ -1,5 +1,5 @@ -#ifndef __CURL_TYPECHECK_GCC_H -#define __CURL_TYPECHECK_GCC_H +#ifndef CURLINC_TYPECHECK_GCC_H +#define CURLINC_TYPECHECK_GCC_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,10 +25,10 @@ /* wraps curl_easy_setopt() with typechecking */ /* To add a new kind of warning, add an - * if(_curl_is_sometype_option(_curl_opt)) - * if(!_curl_is_sometype(value)) + * if(curlcheck_sometype_option(_curl_opt)) + * if(!curlcheck_sometype(value)) * _curl_easy_setopt_err_sometype(); - * block and define _curl_is_sometype_option, _curl_is_sometype and + * block and define curlcheck_sometype_option, curlcheck_sometype and * _curl_easy_setopt_err_sometype below * * NOTE: We use two nested 'if' statements here instead of the && operator, in @@ -38,112 +38,112 @@ * To add an option that uses the same type as an existing option, you'll just * need to extend the appropriate _curl_*_option macro */ -#define curl_easy_setopt(handle, option, value) \ -__extension__ ({ \ - __typeof__(option) _curl_opt = option; \ - if(__builtin_constant_p(_curl_opt)) { \ - if(_curl_is_long_option(_curl_opt)) \ - if(!_curl_is_long(value)) \ - _curl_easy_setopt_err_long(); \ - if(_curl_is_off_t_option(_curl_opt)) \ - if(!_curl_is_off_t(value)) \ - _curl_easy_setopt_err_curl_off_t(); \ - if(_curl_is_string_option(_curl_opt)) \ - if(!_curl_is_string(value)) \ - _curl_easy_setopt_err_string(); \ - if(_curl_is_write_cb_option(_curl_opt)) \ - if(!_curl_is_write_cb(value)) \ - _curl_easy_setopt_err_write_callback(); \ - if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION) \ - if(!_curl_is_resolver_start_callback(value)) \ - _curl_easy_setopt_err_resolver_start_callback(); \ - if((_curl_opt) == CURLOPT_READFUNCTION) \ - if(!_curl_is_read_cb(value)) \ - _curl_easy_setopt_err_read_cb(); \ - if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ - if(!_curl_is_ioctl_cb(value)) \ - _curl_easy_setopt_err_ioctl_cb(); \ - if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ - if(!_curl_is_sockopt_cb(value)) \ - _curl_easy_setopt_err_sockopt_cb(); \ - if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ - if(!_curl_is_opensocket_cb(value)) \ - _curl_easy_setopt_err_opensocket_cb(); \ - if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ - if(!_curl_is_progress_cb(value)) \ - _curl_easy_setopt_err_progress_cb(); \ - if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ - if(!_curl_is_debug_cb(value)) \ - _curl_easy_setopt_err_debug_cb(); \ - if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ - if(!_curl_is_ssl_ctx_cb(value)) \ - _curl_easy_setopt_err_ssl_ctx_cb(); \ - if(_curl_is_conv_cb_option(_curl_opt)) \ - if(!_curl_is_conv_cb(value)) \ - _curl_easy_setopt_err_conv_cb(); \ - if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ - if(!_curl_is_seek_cb(value)) \ - _curl_easy_setopt_err_seek_cb(); \ - if(_curl_is_cb_data_option(_curl_opt)) \ - if(!_curl_is_cb_data(value)) \ - _curl_easy_setopt_err_cb_data(); \ - if((_curl_opt) == CURLOPT_ERRORBUFFER) \ - if(!_curl_is_error_buffer(value)) \ - _curl_easy_setopt_err_error_buffer(); \ - if((_curl_opt) == CURLOPT_STDERR) \ - if(!_curl_is_FILE(value)) \ - _curl_easy_setopt_err_FILE(); \ - if(_curl_is_postfields_option(_curl_opt)) \ - if(!_curl_is_postfields(value)) \ - _curl_easy_setopt_err_postfields(); \ - if((_curl_opt) == CURLOPT_HTTPPOST) \ - if(!_curl_is_arr((value), struct curl_httppost)) \ - _curl_easy_setopt_err_curl_httpost(); \ - if((_curl_opt) == CURLOPT_MIMEPOST) \ - if(!_curl_is_ptr((value), curl_mime)) \ - _curl_easy_setopt_err_curl_mimepost(); \ - if(_curl_is_slist_option(_curl_opt)) \ - if(!_curl_is_arr((value), struct curl_slist)) \ - _curl_easy_setopt_err_curl_slist(); \ - if((_curl_opt) == CURLOPT_SHARE) \ - if(!_curl_is_ptr((value), CURLSH)) \ - _curl_easy_setopt_err_CURLSH(); \ - } \ - curl_easy_setopt(handle, _curl_opt, value); \ -}) +#define curl_easy_setopt(handle, option, value) \ + __extension__({ \ + __typeof__(option) _curl_opt = option; \ + if(__builtin_constant_p(_curl_opt)) { \ + if(curlcheck_long_option(_curl_opt)) \ + if(!curlcheck_long(value)) \ + _curl_easy_setopt_err_long(); \ + if(curlcheck_off_t_option(_curl_opt)) \ + if(!curlcheck_off_t(value)) \ + _curl_easy_setopt_err_curl_off_t(); \ + if(curlcheck_string_option(_curl_opt)) \ + if(!curlcheck_string(value)) \ + _curl_easy_setopt_err_string(); \ + if(curlcheck_write_cb_option(_curl_opt)) \ + if(!curlcheck_write_cb(value)) \ + _curl_easy_setopt_err_write_callback(); \ + if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION) \ + if(!curlcheck_resolver_start_callback(value)) \ + _curl_easy_setopt_err_resolver_start_callback(); \ + if((_curl_opt) == CURLOPT_READFUNCTION) \ + if(!curlcheck_read_cb(value)) \ + _curl_easy_setopt_err_read_cb(); \ + if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ + if(!curlcheck_ioctl_cb(value)) \ + _curl_easy_setopt_err_ioctl_cb(); \ + if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ + if(!curlcheck_sockopt_cb(value)) \ + _curl_easy_setopt_err_sockopt_cb(); \ + if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!curlcheck_opensocket_cb(value)) \ + _curl_easy_setopt_err_opensocket_cb(); \ + if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ + if(!curlcheck_progress_cb(value)) \ + _curl_easy_setopt_err_progress_cb(); \ + if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ + if(!curlcheck_debug_cb(value)) \ + _curl_easy_setopt_err_debug_cb(); \ + if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!curlcheck_ssl_ctx_cb(value)) \ + _curl_easy_setopt_err_ssl_ctx_cb(); \ + if(curlcheck_conv_cb_option(_curl_opt)) \ + if(!curlcheck_conv_cb(value)) \ + _curl_easy_setopt_err_conv_cb(); \ + if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ + if(!curlcheck_seek_cb(value)) \ + _curl_easy_setopt_err_seek_cb(); \ + if(curlcheck_cb_data_option(_curl_opt)) \ + if(!curlcheck_cb_data(value)) \ + _curl_easy_setopt_err_cb_data(); \ + if((_curl_opt) == CURLOPT_ERRORBUFFER) \ + if(!curlcheck_error_buffer(value)) \ + _curl_easy_setopt_err_error_buffer(); \ + if((_curl_opt) == CURLOPT_STDERR) \ + if(!curlcheck_FILE(value)) \ + _curl_easy_setopt_err_FILE(); \ + if(curlcheck_postfields_option(_curl_opt)) \ + if(!curlcheck_postfields(value)) \ + _curl_easy_setopt_err_postfields(); \ + if((_curl_opt) == CURLOPT_HTTPPOST) \ + if(!curlcheck_arr((value), struct curl_httppost)) \ + _curl_easy_setopt_err_curl_httpost(); \ + if((_curl_opt) == CURLOPT_MIMEPOST) \ + if(!curlcheck_ptr((value), curl_mime)) \ + _curl_easy_setopt_err_curl_mimepost(); \ + if(curlcheck_slist_option(_curl_opt)) \ + if(!curlcheck_arr((value), struct curl_slist)) \ + _curl_easy_setopt_err_curl_slist(); \ + if((_curl_opt) == CURLOPT_SHARE) \ + if(!curlcheck_ptr((value), CURLSH)) \ + _curl_easy_setopt_err_CURLSH(); \ + } \ + curl_easy_setopt(handle, _curl_opt, value); \ + }) /* wraps curl_easy_getinfo() with typechecking */ -#define curl_easy_getinfo(handle, info, arg) \ -__extension__ ({ \ - __typeof__(info) _curl_info = info; \ - if(__builtin_constant_p(_curl_info)) { \ - if(_curl_is_string_info(_curl_info)) \ - if(!_curl_is_arr((arg), char *)) \ - _curl_easy_getinfo_err_string(); \ - if(_curl_is_long_info(_curl_info)) \ - if(!_curl_is_arr((arg), long)) \ - _curl_easy_getinfo_err_long(); \ - if(_curl_is_double_info(_curl_info)) \ - if(!_curl_is_arr((arg), double)) \ - _curl_easy_getinfo_err_double(); \ - if(_curl_is_slist_info(_curl_info)) \ - if(!_curl_is_arr((arg), struct curl_slist *)) \ - _curl_easy_getinfo_err_curl_slist(); \ - if(_curl_is_tlssessioninfo_info(_curl_info)) \ - if(!_curl_is_arr((arg), struct curl_tlssessioninfo *)) \ - _curl_easy_getinfo_err_curl_tlssesssioninfo(); \ - if(_curl_is_certinfo_info(_curl_info)) \ - if(!_curl_is_arr((arg), struct curl_certinfo *)) \ - _curl_easy_getinfo_err_curl_certinfo(); \ - if(_curl_is_socket_info(_curl_info)) \ - if(!_curl_is_arr((arg), curl_socket_t)) \ - _curl_easy_getinfo_err_curl_socket(); \ - if(_curl_is_off_t_info(_curl_info)) \ - if(!_curl_is_arr((arg), curl_off_t)) \ - _curl_easy_getinfo_err_curl_off_t(); \ - } \ - curl_easy_getinfo(handle, _curl_info, arg); \ -}) +#define curl_easy_getinfo(handle, info, arg) \ + __extension__({ \ + __typeof__(info) _curl_info = info; \ + if(__builtin_constant_p(_curl_info)) { \ + if(curlcheck_string_info(_curl_info)) \ + if(!curlcheck_arr((arg), char *)) \ + _curl_easy_getinfo_err_string(); \ + if(curlcheck_long_info(_curl_info)) \ + if(!curlcheck_arr((arg), long)) \ + _curl_easy_getinfo_err_long(); \ + if(curlcheck_double_info(_curl_info)) \ + if(!curlcheck_arr((arg), double)) \ + _curl_easy_getinfo_err_double(); \ + if(curlcheck_slist_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_slist *)) \ + _curl_easy_getinfo_err_curl_slist(); \ + if(curlcheck_tlssessioninfo_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ + _curl_easy_getinfo_err_curl_tlssesssioninfo(); \ + if(curlcheck_certinfo_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_certinfo *)) \ + _curl_easy_getinfo_err_curl_certinfo(); \ + if(curlcheck_socket_info(_curl_info)) \ + if(!curlcheck_arr((arg), curl_socket_t)) \ + _curl_easy_getinfo_err_curl_socket(); \ + if(curlcheck_off_t_info(_curl_info)) \ + if(!curlcheck_arr((arg), curl_off_t)) \ + _curl_easy_getinfo_err_curl_off_t(); \ + } \ + curl_easy_getinfo(handle, _curl_info, arg); \ + }) /* * For now, just make sure that the functions are called with three arguments @@ -156,83 +156,83 @@ __extension__ ({ \ * functions */ /* To define a new warning, use _CURL_WARNING(identifier, "message") */ -#define _CURL_WARNING(id, message) \ - static void __attribute__((__warning__(message))) \ - __attribute__((__unused__)) __attribute__((__noinline__)) \ +#define CURLWARNING(id, message) \ + static void __attribute__((__warning__(message))) \ + __attribute__((__unused__)) __attribute__((__noinline__)) \ id(void) { __asm__(""); } -_CURL_WARNING(_curl_easy_setopt_err_long, +CURLWARNING(_curl_easy_setopt_err_long, "curl_easy_setopt expects a long argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_curl_off_t, +CURLWARNING(_curl_easy_setopt_err_curl_off_t, "curl_easy_setopt expects a curl_off_t argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_string, +CURLWARNING(_curl_easy_setopt_err_string, "curl_easy_setopt expects a " "string ('char *' or char[]) argument for this option" ) -_CURL_WARNING(_curl_easy_setopt_err_write_callback, +CURLWARNING(_curl_easy_setopt_err_write_callback, "curl_easy_setopt expects a curl_write_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_resolver_start_callback, +CURLWARNING(_curl_easy_setopt_err_resolver_start_callback, "curl_easy_setopt expects a " "curl_resolver_start_callback argument for this option" ) -_CURL_WARNING(_curl_easy_setopt_err_read_cb, +CURLWARNING(_curl_easy_setopt_err_read_cb, "curl_easy_setopt expects a curl_read_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb, +CURLWARNING(_curl_easy_setopt_err_ioctl_cb, "curl_easy_setopt expects a curl_ioctl_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb, +CURLWARNING(_curl_easy_setopt_err_sockopt_cb, "curl_easy_setopt expects a curl_sockopt_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb, +CURLWARNING(_curl_easy_setopt_err_opensocket_cb, "curl_easy_setopt expects a " "curl_opensocket_callback argument for this option" ) -_CURL_WARNING(_curl_easy_setopt_err_progress_cb, +CURLWARNING(_curl_easy_setopt_err_progress_cb, "curl_easy_setopt expects a curl_progress_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_debug_cb, +CURLWARNING(_curl_easy_setopt_err_debug_cb, "curl_easy_setopt expects a curl_debug_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb, +CURLWARNING(_curl_easy_setopt_err_ssl_ctx_cb, "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_conv_cb, +CURLWARNING(_curl_easy_setopt_err_conv_cb, "curl_easy_setopt expects a curl_conv_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_seek_cb, +CURLWARNING(_curl_easy_setopt_err_seek_cb, "curl_easy_setopt expects a curl_seek_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_cb_data, +CURLWARNING(_curl_easy_setopt_err_cb_data, "curl_easy_setopt expects a " "private data pointer as argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_error_buffer, +CURLWARNING(_curl_easy_setopt_err_error_buffer, "curl_easy_setopt expects a " "char buffer of CURL_ERROR_SIZE as argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_FILE, +CURLWARNING(_curl_easy_setopt_err_FILE, "curl_easy_setopt expects a 'FILE *' argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_postfields, +CURLWARNING(_curl_easy_setopt_err_postfields, "curl_easy_setopt expects a 'void *' or 'char *' argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_curl_httpost, +CURLWARNING(_curl_easy_setopt_err_curl_httpost, "curl_easy_setopt expects a 'struct curl_httppost *' " "argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_curl_mimepost, +CURLWARNING(_curl_easy_setopt_err_curl_mimepost, "curl_easy_setopt expects a 'curl_mime *' " "argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_curl_slist, +CURLWARNING(_curl_easy_setopt_err_curl_slist, "curl_easy_setopt expects a 'struct curl_slist *' argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_CURLSH, +CURLWARNING(_curl_easy_setopt_err_CURLSH, "curl_easy_setopt expects a CURLSH* argument for this option") -_CURL_WARNING(_curl_easy_getinfo_err_string, +CURLWARNING(_curl_easy_getinfo_err_string, "curl_easy_getinfo expects a pointer to 'char *' for this info") -_CURL_WARNING(_curl_easy_getinfo_err_long, +CURLWARNING(_curl_easy_getinfo_err_long, "curl_easy_getinfo expects a pointer to long for this info") -_CURL_WARNING(_curl_easy_getinfo_err_double, +CURLWARNING(_curl_easy_getinfo_err_double, "curl_easy_getinfo expects a pointer to double for this info") -_CURL_WARNING(_curl_easy_getinfo_err_curl_slist, +CURLWARNING(_curl_easy_getinfo_err_curl_slist, "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info") -_CURL_WARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo, +CURLWARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo, "curl_easy_getinfo expects a pointer to " "'struct curl_tlssessioninfo *' for this info") -_CURL_WARNING(_curl_easy_getinfo_err_curl_certinfo, +CURLWARNING(_curl_easy_getinfo_err_curl_certinfo, "curl_easy_getinfo expects a pointer to " "'struct curl_certinfo *' for this info") -_CURL_WARNING(_curl_easy_getinfo_err_curl_socket, +CURLWARNING(_curl_easy_getinfo_err_curl_socket, "curl_easy_getinfo expects a pointer to curl_socket_t for this info") -_CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, +CURLWARNING(_curl_easy_getinfo_err_curl_off_t, "curl_easy_getinfo expects a pointer to curl_off_t for this info") /* groups of curl_easy_setops options that take the same type of argument */ @@ -244,14 +244,14 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, */ /* evaluates to true if option takes a long argument */ -#define _curl_is_long_option(option) \ +#define curlcheck_long_option(option) \ (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) -#define _curl_is_off_t_option(option) \ - ((option) > CURLOPTTYPE_OFF_T) +#define curlcheck_off_t_option(option) \ + (((option) > CURLOPTTYPE_OFF_T) && ((option) < CURLOPTTYPE_BLOB)) /* evaluates to true if option takes a char* argument */ -#define _curl_is_string_option(option) \ +#define curlcheck_string_option(option) \ ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \ (option) == CURLOPT_ACCEPT_ENCODING || \ (option) == CURLOPT_ALTSVC || \ @@ -300,15 +300,18 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_PROXY_SSLKEY || \ (option) == CURLOPT_PROXY_SSLKEYTYPE || \ (option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \ + (option) == CURLOPT_PROXY_TLS13_CIPHERS || \ (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \ - (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \ (option) == CURLOPT_PROXY_TLSAUTH_TYPE || \ + (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \ (option) == CURLOPT_RANDOM_FILE || \ (option) == CURLOPT_RANGE || \ (option) == CURLOPT_REFERER || \ + (option) == CURLOPT_REQUEST_TARGET || \ (option) == CURLOPT_RTSP_SESSION_ID || \ (option) == CURLOPT_RTSP_STREAM_URI || \ (option) == CURLOPT_RTSP_TRANSPORT || \ + (option) == CURLOPT_SASL_AUTHZID || \ (option) == CURLOPT_SERVICE_NAME || \ (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ @@ -321,6 +324,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_SSLKEY || \ (option) == CURLOPT_SSLKEYTYPE || \ (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_TLS13_CIPHERS || \ (option) == CURLOPT_TLSAUTH_PASSWORD || \ (option) == CURLOPT_TLSAUTH_TYPE || \ (option) == CURLOPT_TLSAUTH_USERNAME || \ @@ -333,18 +337,18 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, 0) /* evaluates to true if option takes a curl_write_callback argument */ -#define _curl_is_write_cb_option(option) \ - ((option) == CURLOPT_HEADERFUNCTION || \ +#define curlcheck_write_cb_option(option) \ + ((option) == CURLOPT_HEADERFUNCTION || \ (option) == CURLOPT_WRITEFUNCTION) /* evaluates to true if option takes a curl_conv_callback argument */ -#define _curl_is_conv_cb_option(option) \ - ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ - (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ +#define curlcheck_conv_cb_option(option) \ + ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) /* evaluates to true if option takes a data argument to pass to a callback */ -#define _curl_is_cb_data_option(option) \ +#define curlcheck_cb_data_option(option) \ ((option) == CURLOPT_CHUNK_DATA || \ (option) == CURLOPT_CLOSESOCKETDATA || \ (option) == CURLOPT_DEBUGDATA || \ @@ -362,17 +366,17 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_SSL_CTX_DATA || \ (option) == CURLOPT_WRITEDATA || \ (option) == CURLOPT_RESOLVER_START_DATA || \ - (option) == CURLOPT_CURLU || \ + (option) == CURLOPT_TRAILERDATA || \ 0) /* evaluates to true if option takes a POST data argument (void* or char*) */ -#define _curl_is_postfields_option(option) \ +#define curlcheck_postfields_option(option) \ ((option) == CURLOPT_POSTFIELDS || \ (option) == CURLOPT_COPYPOSTFIELDS || \ 0) /* evaluates to true if option takes a struct curl_slist * argument */ -#define _curl_is_slist_option(option) \ +#define curlcheck_slist_option(option) \ ((option) == CURLOPT_HTTP200ALIASES || \ (option) == CURLOPT_HTTPHEADER || \ (option) == CURLOPT_MAIL_RCPT || \ @@ -382,45 +386,47 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_QUOTE || \ (option) == CURLOPT_RESOLVE || \ (option) == CURLOPT_TELNETOPTIONS || \ + (option) == CURLOPT_CONNECT_TO || \ 0) /* groups of curl_easy_getinfo infos that take the same type of argument */ /* evaluates to true if info expects a pointer to char * argument */ -#define _curl_is_string_info(info) \ - (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG) +#define curlcheck_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG && \ + (info) != CURLINFO_PRIVATE) /* evaluates to true if info expects a pointer to long argument */ -#define _curl_is_long_info(info) \ +#define curlcheck_long_info(info) \ (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) /* evaluates to true if info expects a pointer to double argument */ -#define _curl_is_double_info(info) \ +#define curlcheck_double_info(info) \ (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) /* true if info expects a pointer to struct curl_slist * argument */ -#define _curl_is_slist_info(info) \ +#define curlcheck_slist_info(info) \ (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST)) /* true if info expects a pointer to struct curl_tlssessioninfo * argument */ -#define _curl_is_tlssessioninfo_info(info) \ +#define curlcheck_tlssessioninfo_info(info) \ (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION)) /* true if info expects a pointer to struct curl_certinfo * argument */ -#define _curl_is_certinfo_info(info) ((info) == CURLINFO_CERTINFO) +#define curlcheck_certinfo_info(info) ((info) == CURLINFO_CERTINFO) /* true if info expects a pointer to struct curl_socket_t argument */ -#define _curl_is_socket_info(info) \ +#define curlcheck_socket_info(info) \ (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T) /* true if info expects a pointer to curl_off_t argument */ -#define _curl_is_off_t_info(info) \ +#define curlcheck_off_t_info(info) \ (CURLINFO_OFF_T < (info)) /* typecheck helpers -- check whether given expression has requested type*/ -/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros, +/* For pointers, you can use the curlcheck_ptr/curlcheck_arr macros, * otherwise define a new macro. Search for __builtin_types_compatible_p * in the GCC manual. * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is @@ -430,35 +436,35 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, */ /* XXX: should evaluate to true if expr is a pointer */ -#define _curl_is_any_ptr(expr) \ +#define curlcheck_any_ptr(expr) \ (sizeof(expr) == sizeof(void *)) /* evaluates to true if expr is NULL */ /* XXX: must not evaluate expr, so this check is not accurate */ -#define _curl_is_NULL(expr) \ +#define curlcheck_NULL(expr) \ (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL))) /* evaluates to true if expr is type*, const type* or NULL */ -#define _curl_is_ptr(expr, type) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), type *) || \ +#define curlcheck_ptr(expr, type) \ + (curlcheck_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), type *) || \ __builtin_types_compatible_p(__typeof__(expr), const type *)) /* evaluates to true if expr is one of type[], type*, NULL or const type* */ -#define _curl_is_arr(expr, type) \ - (_curl_is_ptr((expr), type) || \ +#define curlcheck_arr(expr, type) \ + (curlcheck_ptr((expr), type) || \ __builtin_types_compatible_p(__typeof__(expr), type [])) /* evaluates to true if expr is a string */ -#define _curl_is_string(expr) \ - (_curl_is_arr((expr), char) || \ - _curl_is_arr((expr), signed char) || \ - _curl_is_arr((expr), unsigned char)) +#define curlcheck_string(expr) \ + (curlcheck_arr((expr), char) || \ + curlcheck_arr((expr), signed char) || \ + curlcheck_arr((expr), unsigned char)) /* evaluates to true if expr is a long (no matter the signedness) * XXX: for now, int is also accepted (and therefore short and char, which * are promoted to int when passed to a variadic function) */ -#define _curl_is_long(expr) \ +#define curlcheck_long(expr) \ (__builtin_types_compatible_p(__typeof__(expr), long) || \ __builtin_types_compatible_p(__typeof__(expr), signed long) || \ __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ @@ -473,59 +479,59 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, __builtin_types_compatible_p(__typeof__(expr), unsigned char)) /* evaluates to true if expr is of type curl_off_t */ -#define _curl_is_off_t(expr) \ +#define curlcheck_off_t(expr) \ (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) /* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ /* XXX: also check size of an char[] array? */ -#define _curl_is_error_buffer(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), char *) || \ +#define curlcheck_error_buffer(expr) \ + (curlcheck_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), char *) || \ __builtin_types_compatible_p(__typeof__(expr), char[])) /* evaluates to true if expr is of type (const) void* or (const) FILE* */ #if 0 -#define _curl_is_cb_data(expr) \ - (_curl_is_ptr((expr), void) || \ - _curl_is_ptr((expr), FILE)) +#define curlcheck_cb_data(expr) \ + (curlcheck_ptr((expr), void) || \ + curlcheck_ptr((expr), FILE)) #else /* be less strict */ -#define _curl_is_cb_data(expr) \ - _curl_is_any_ptr(expr) +#define curlcheck_cb_data(expr) \ + curlcheck_any_ptr(expr) #endif /* evaluates to true if expr is of type FILE* */ -#define _curl_is_FILE(expr) \ - (_curl_is_NULL(expr) || \ +#define curlcheck_FILE(expr) \ + (curlcheck_NULL(expr) || \ (__builtin_types_compatible_p(__typeof__(expr), FILE *))) /* evaluates to true if expr can be passed as POST data (void* or char*) */ -#define _curl_is_postfields(expr) \ - (_curl_is_ptr((expr), void) || \ - _curl_is_arr((expr), char) || \ - _curl_is_arr((expr), unsigned char)) +#define curlcheck_postfields(expr) \ + (curlcheck_ptr((expr), void) || \ + curlcheck_arr((expr), char) || \ + curlcheck_arr((expr), unsigned char)) /* helper: __builtin_types_compatible_p distinguishes between functions and * function pointers, hide it */ -#define _curl_callback_compatible(func, type) \ - (__builtin_types_compatible_p(__typeof__(func), type) || \ +#define curlcheck_cb_compatible(func, type) \ + (__builtin_types_compatible_p(__typeof__(func), type) || \ __builtin_types_compatible_p(__typeof__(func) *, type)) /* evaluates to true if expr is of type curl_resolver_start_callback */ -#define _curl_is_resolver_start_callback(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), curl_resolver_start_callback)) +#define curlcheck_resolver_start_callback(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_resolver_start_callback)) /* evaluates to true if expr is of type curl_read_callback or "similar" */ -#define _curl_is_read_cb(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), __typeof__(fread) *) || \ - _curl_callback_compatible((expr), curl_read_callback) || \ - _curl_callback_compatible((expr), _curl_read_callback1) || \ - _curl_callback_compatible((expr), _curl_read_callback2) || \ - _curl_callback_compatible((expr), _curl_read_callback3) || \ - _curl_callback_compatible((expr), _curl_read_callback4) || \ - _curl_callback_compatible((expr), _curl_read_callback5) || \ - _curl_callback_compatible((expr), _curl_read_callback6)) +#define curlcheck_read_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), __typeof__(fread) *) || \ + curlcheck_cb_compatible((expr), curl_read_callback) || \ + curlcheck_cb_compatible((expr), _curl_read_callback1) || \ + curlcheck_cb_compatible((expr), _curl_read_callback2) || \ + curlcheck_cb_compatible((expr), _curl_read_callback3) || \ + curlcheck_cb_compatible((expr), _curl_read_callback4) || \ + curlcheck_cb_compatible((expr), _curl_read_callback5) || \ + curlcheck_cb_compatible((expr), _curl_read_callback6)) typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *); typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *); typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *); @@ -534,16 +540,16 @@ typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *); typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_write_callback or "similar" */ -#define _curl_is_write_cb(expr) \ - (_curl_is_read_cb(expr) || \ - _curl_callback_compatible((expr), __typeof__(fwrite) *) || \ - _curl_callback_compatible((expr), curl_write_callback) || \ - _curl_callback_compatible((expr), _curl_write_callback1) || \ - _curl_callback_compatible((expr), _curl_write_callback2) || \ - _curl_callback_compatible((expr), _curl_write_callback3) || \ - _curl_callback_compatible((expr), _curl_write_callback4) || \ - _curl_callback_compatible((expr), _curl_write_callback5) || \ - _curl_callback_compatible((expr), _curl_write_callback6)) +#define curlcheck_write_cb(expr) \ + (curlcheck_read_cb(expr) || \ + curlcheck_cb_compatible((expr), __typeof__(fwrite) *) || \ + curlcheck_cb_compatible((expr), curl_write_callback) || \ + curlcheck_cb_compatible((expr), _curl_write_callback1) || \ + curlcheck_cb_compatible((expr), _curl_write_callback2) || \ + curlcheck_cb_compatible((expr), _curl_write_callback3) || \ + curlcheck_cb_compatible((expr), _curl_write_callback4) || \ + curlcheck_cb_compatible((expr), _curl_write_callback5) || \ + curlcheck_cb_compatible((expr), _curl_write_callback6)) typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *); typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t, const void *); @@ -554,37 +560,37 @@ typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t, typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ -#define _curl_is_ioctl_cb(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), curl_ioctl_callback) || \ - _curl_callback_compatible((expr), _curl_ioctl_callback1) || \ - _curl_callback_compatible((expr), _curl_ioctl_callback2) || \ - _curl_callback_compatible((expr), _curl_ioctl_callback3) || \ - _curl_callback_compatible((expr), _curl_ioctl_callback4)) +#define curlcheck_ioctl_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_ioctl_callback) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback1) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback2) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback3) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback4)) typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *); typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *); typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *); typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *); /* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ -#define _curl_is_sockopt_cb(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), curl_sockopt_callback) || \ - _curl_callback_compatible((expr), _curl_sockopt_callback1) || \ - _curl_callback_compatible((expr), _curl_sockopt_callback2)) +#define curlcheck_sockopt_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_sockopt_callback) || \ + curlcheck_cb_compatible((expr), _curl_sockopt_callback1) || \ + curlcheck_cb_compatible((expr), _curl_sockopt_callback2)) typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t, curlsocktype); /* evaluates to true if expr is of type curl_opensocket_callback or "similar" */ -#define _curl_is_opensocket_cb(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), curl_opensocket_callback) || \ - _curl_callback_compatible((expr), _curl_opensocket_callback1) || \ - _curl_callback_compatible((expr), _curl_opensocket_callback2) || \ - _curl_callback_compatible((expr), _curl_opensocket_callback3) || \ - _curl_callback_compatible((expr), _curl_opensocket_callback4)) +#define curlcheck_opensocket_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_opensocket_callback) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback1) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback2) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback3) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback4)) typedef curl_socket_t (*_curl_opensocket_callback1) (void *, curlsocktype, struct curl_sockaddr *); typedef curl_socket_t (*_curl_opensocket_callback2) @@ -595,28 +601,28 @@ typedef curl_socket_t (*_curl_opensocket_callback4) (const void *, curlsocktype, const struct curl_sockaddr *); /* evaluates to true if expr is of type curl_progress_callback or "similar" */ -#define _curl_is_progress_cb(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), curl_progress_callback) || \ - _curl_callback_compatible((expr), _curl_progress_callback1) || \ - _curl_callback_compatible((expr), _curl_progress_callback2)) +#define curlcheck_progress_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_progress_callback) || \ + curlcheck_cb_compatible((expr), _curl_progress_callback1) || \ + curlcheck_cb_compatible((expr), _curl_progress_callback2)) typedef int (*_curl_progress_callback1)(void *, double, double, double, double); typedef int (*_curl_progress_callback2)(const void *, double, double, double, double); /* evaluates to true if expr is of type curl_debug_callback or "similar" */ -#define _curl_is_debug_cb(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), curl_debug_callback) || \ - _curl_callback_compatible((expr), _curl_debug_callback1) || \ - _curl_callback_compatible((expr), _curl_debug_callback2) || \ - _curl_callback_compatible((expr), _curl_debug_callback3) || \ - _curl_callback_compatible((expr), _curl_debug_callback4) || \ - _curl_callback_compatible((expr), _curl_debug_callback5) || \ - _curl_callback_compatible((expr), _curl_debug_callback6) || \ - _curl_callback_compatible((expr), _curl_debug_callback7) || \ - _curl_callback_compatible((expr), _curl_debug_callback8)) +#define curlcheck_debug_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_debug_callback) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback1) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback2) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback3) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback4) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback5) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback6) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback7) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback8)) typedef int (*_curl_debug_callback1) (CURL *, curl_infotype, char *, size_t, void *); typedef int (*_curl_debug_callback2) (CURL *, @@ -636,17 +642,17 @@ typedef int (*_curl_debug_callback8) (CURL *, /* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ /* this is getting even messier... */ -#define _curl_is_ssl_ctx_cb(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), curl_ssl_ctx_callback) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback8)) +#define curlcheck_ssl_ctx_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_ssl_ctx_callback) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback1) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback2) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback3) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback4) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback5) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback6) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback7) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback8)) typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *); typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *); typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *); @@ -669,26 +675,26 @@ typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; #endif /* evaluates to true if expr is of type curl_conv_callback or "similar" */ -#define _curl_is_conv_cb(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), curl_conv_callback) || \ - _curl_callback_compatible((expr), _curl_conv_callback1) || \ - _curl_callback_compatible((expr), _curl_conv_callback2) || \ - _curl_callback_compatible((expr), _curl_conv_callback3) || \ - _curl_callback_compatible((expr), _curl_conv_callback4)) +#define curlcheck_conv_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_conv_callback) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback1) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback2) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback3) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback4)) typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); /* evaluates to true if expr is of type curl_seek_callback or "similar" */ -#define _curl_is_seek_cb(expr) \ - (_curl_is_NULL(expr) || \ - _curl_callback_compatible((expr), curl_seek_callback) || \ - _curl_callback_compatible((expr), _curl_seek_callback1) || \ - _curl_callback_compatible((expr), _curl_seek_callback2)) +#define curlcheck_seek_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_seek_callback) || \ + curlcheck_cb_compatible((expr), _curl_seek_callback1) || \ + curlcheck_cb_compatible((expr), _curl_seek_callback2)) typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); -#endif /* __CURL_TYPECHECK_GCC_H */ +#endif /* CURLINC_TYPECHECK_GCC_H */ diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h index 58e89d85c2a..f2d06770dc8 100644 --- a/Utilities/cmcurl/include/curl/urlapi.h +++ b/Utilities/cmcurl/include/curl/urlapi.h @@ -1,5 +1,5 @@ -#ifndef __CURL_URLAPI_H -#define __CURL_URLAPI_H +#ifndef CURLINC_URLAPI_H +#define CURLINC_URLAPI_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -77,6 +77,8 @@ typedef enum { #define CURLU_URLENCODE (1<<7) /* URL encode on set */ #define CURLU_APPENDQUERY (1<<8) /* append a form style part */ #define CURLU_GUESS_SCHEME (1<<9) /* legacy curl-style guessing */ +#define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the + scheme is unknown. */ typedef struct Curl_URL CURLU; @@ -120,4 +122,4 @@ CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, } /* end of extern "C" */ #endif -#endif +#endif /* CURLINC_URLAPI_H */ diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index 2a6279c29a9..32bea68c3de 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -1,3 +1,24 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.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. +# +########################################################################### set(LIB_NAME cmcurl) if(BUILD_SHARED_LIBS) @@ -20,7 +41,6 @@ list(APPEND HHEADERS if(MSVC AND NOT CURL_STATICLIB) list(APPEND CSOURCES libcurl.rc) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4127") endif() # SET(CSOURCES @@ -97,11 +117,16 @@ add_library( ${CMAKE_CURL_SSL_DLLS} ) +add_library( + ${PROJECT_NAME}::${LIB_NAME} + ALIAS ${LIB_NAME} + ) + if(NOT BUILD_SHARED_LIBS) set_target_properties(${LIB_NAME} PROPERTIES INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB) endif() -target_link_libraries(${LIB_NAME} ${CURL_LIBS}) +target_link_libraries(${LIB_NAME} PRIVATE ${CURL_LIBS}) if(0) # This code not needed for building within CMake. if(WIN32) @@ -121,6 +146,12 @@ endif() set_target_properties(${LIB_NAME} PROPERTIES PREFIX "") set_target_properties(${LIB_NAME} PROPERTIES IMPORT_PREFIX "") +if(CURL_HAS_LTO) + set_target_properties(${LIB_NAME} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE + INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) +endif() + if(WIN32) if(BUILD_SHARED_LIBS) # Add "_imp" as a suffix before the extension to avoid conflicting with the statically linked "libcurl.lib" @@ -141,7 +172,7 @@ install(TARGETS ${LIB_NAME} export(TARGETS ${LIB_NAME} APPEND FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake - NAMESPACE CURL:: + NAMESPACE ${PROJECT_NAME}:: ) endif() diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc index 235b82b0e3b..723b826e90e 100644 --- a/Utilities/cmcurl/lib/Makefile.inc +++ b/Utilities/cmcurl/lib/Makefile.inc @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. +# Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -20,65 +20,70 @@ # ########################################################################### -LIB_VAUTH_CFILES = vauth/vauth.c vauth/cleartext.c vauth/cram.c \ - vauth/digest.c vauth/digest_sspi.c vauth/krb5_gssapi.c \ - vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c vauth/oauth2.c \ - vauth/spnego_gssapi.c vauth/spnego_sspi.c +LIB_VAUTH_CFILES = vauth/cleartext.c vauth/cram.c vauth/digest.c \ + vauth/digest_sspi.c vauth/krb5_gssapi.c vauth/krb5_sspi.c vauth/ntlm.c \ + vauth/ntlm_sspi.c vauth/oauth2.c vauth/spnego_gssapi.c vauth/spnego_sspi.c \ + vauth/vauth.c -LIB_VAUTH_HFILES = vauth/vauth.h vauth/digest.h vauth/ntlm.h +LIB_VAUTH_HFILES = vauth/digest.h vauth/ntlm.h vauth/vauth.h -LIB_VTLS_CFILES = vtls/openssl.c vtls/gtls.c vtls/vtls.c vtls/nss.c \ - vtls/polarssl.c vtls/polarssl_threadlock.c \ - vtls/cyassl.c vtls/schannel.c vtls/schannel_verify.c \ - vtls/sectransp.c vtls/gskit.c vtls/mbedtls.c vtls/mesalink.c +LIB_VTLS_CFILES = vtls/bearssl.c vtls/gskit.c vtls/gtls.c vtls/keylog.c \ + vtls/mbedtls.c vtls/mbedtls_threadlock.c vtls/mesalink.c vtls/nss.c \ + vtls/openssl.c vtls/schannel.c vtls/schannel_verify.c vtls/sectransp.c \ + vtls/vtls.c vtls/wolfssl.c -LIB_VTLS_HFILES = vtls/openssl.h vtls/vtls.h vtls/gtls.h \ - vtls/nssg.h vtls/polarssl.h vtls/polarssl_threadlock.h \ - vtls/cyassl.h vtls/schannel.h vtls/sectransp.h vtls/gskit.h \ - vtls/mbedtls.h vtls/mesalink.h +LIB_VTLS_HFILES = vtls/bearssl.h vtls/gskit.h vtls/gtls.h vtls/keylog.h \ + vtls/mbedtls.h vtls/mbedtls_threadlock.h vtls/mesalink.h vtls/nssg.h \ + vtls/openssl.h vtls/schannel.h vtls/sectransp.h vtls/vtls.h vtls/wolfssl.h -LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ - cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c \ - ldap.c version.c getenv.c escape.c mprintf.c telnet.c netrc.c \ - getinfo.c transfer.c strcase.c easy.c security.c curl_fnmatch.c \ - fileinfo.c ftplistparser.c wildcard.c krb5.c memdebug.c http_chunks.c \ - strtok.c connect.c llist.c hash.c multi.c content_encoding.c share.c \ - http_digest.c md4.c md5.c http_negotiate.c inet_pton.c strtoofft.c \ - strerror.c amigaos.c hostasyn.c hostip4.c hostip6.c hostsyn.c \ - inet_ntop.c parsedate.c select.c tftp.c splay.c strdup.c socks.c \ - ssh.c ssh-libssh.c curl_addrinfo.c socks_gssapi.c socks_sspi.c \ - curl_sspi.c slist.c nonblock.c curl_memrchr.c imap.c pop3.c smtp.c \ - pingpong.c rtsp.c curl_threads.c warnless.c hmac.c curl_rtmp.c \ - openldap.c curl_gethostname.c gopher.c idn_win32.c \ - http_proxy.c non-ascii.c asyn-ares.c asyn-thread.c curl_gssapi.c \ - http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c \ - curl_multibyte.c hostcheck.c conncache.c dotdot.c \ - x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \ - mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c \ - doh.c urlapi.c curl_get_line.c altsvc.c +LIB_VQUIC_CFILES = vquic/ngtcp2.c vquic/quiche.c vquic/vquic.c -LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \ - formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \ - speedcheck.h urldata.h curl_ldap.h escape.h telnet.h getinfo.h \ - strcase.h curl_sec.h memdebug.h http_chunks.h curl_fnmatch.h \ - wildcard.h fileinfo.h ftplistparser.h strtok.h connect.h llist.h \ - hash.h content_encoding.h share.h curl_md4.h curl_md5.h http_digest.h \ - http_negotiate.h inet_pton.h amigaos.h strtoofft.h strerror.h \ - inet_ntop.h curlx.h curl_memory.h curl_setup.h transfer.h select.h \ - easyif.h multiif.h parsedate.h tftp.h sockaddr.h splay.h strdup.h \ - socks.h ssh.h curl_base64.h curl_addrinfo.h curl_sspi.h \ - slist.h nonblock.h curl_memrchr.h imap.h pop3.h smtp.h pingpong.h \ - rtsp.h curl_threads.h warnless.h curl_hmac.h curl_rtmp.h \ - curl_gethostname.h gopher.h http_proxy.h non-ascii.h asyn.h \ - http_ntlm.h curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h \ - curl_sasl.h curl_multibyte.h hostcheck.h conncache.h \ - curl_setup_once.h multihandle.h setup-vms.h dotdot.h \ - x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \ - curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h \ - curl_path.h curl_ctype.h curl_range.h psl.h doh.h urlapi-int.h \ - curl_get_line.h altsvc.h +LIB_VQUIC_HFILES = vquic/ngtcp2.h vquic/quiche.h vquic/vquic.h + +LIB_VSSH_CFILES = vssh/libssh.c vssh/libssh2.c vssh/wolfssh.c + +LIB_VSSH_HFILES = vssh/ssh.h + +LIB_CFILES = altsvc.c amigaos.c asyn-ares.c asyn-thread.c base64.c \ + conncache.c connect.c content_encoding.c cookie.c curl_addrinfo.c \ + curl_ctype.c curl_des.c curl_endian.c curl_fnmatch.c curl_get_line.c \ + curl_gethostname.c curl_gssapi.c curl_memrchr.c curl_multibyte.c \ + curl_ntlm_core.c curl_ntlm_wb.c curl_path.c curl_range.c curl_rtmp.c \ + curl_sasl.c curl_sspi.c curl_threads.c dict.c dotdot.c easy.c escape.c \ + file.c fileinfo.c formdata.c ftp.c url.c ftplistparser.c getenv.c getinfo.c \ + gopher.c hash.c hmac.c hostasyn.c hostcheck.c hostip.c hostip4.c hostip6.c \ + hostsyn.c http.c http2.c http_chunks.c http_digest.c http_negotiate.c \ + http_ntlm.c http_proxy.c idn_win32.c if2ip.c imap.c inet_ntop.c inet_pton.c \ + krb5.c ldap.c llist.c md4.c md5.c memdebug.c mime.c mprintf.c mqtt.c \ + multi.c netrc.c non-ascii.c nonblock.c openldap.c parsedate.c pingpong.c \ + pop3.c progress.c psl.c doh.c rand.c rename.c rtsp.c security.c select.c \ + sendf.c setopt.c sha256.c share.c slist.c smb.c smtp.c socketpair.c socks.c \ + socks_gssapi.c socks_sspi.c speedcheck.c splay.c strcase.c strdup.c \ + strerror.c strtok.c strtoofft.c system_win32.c telnet.c tftp.c timeval.c \ + transfer.c urlapi.c version.c warnless.c wildcard.c x509asn1.c dynbuf.c + +LIB_HFILES = altsvc.h amigaos.h arpa_telnet.h asyn.h conncache.h connect.h \ + content_encoding.h cookie.h curl_addrinfo.h curl_base64.h curl_ctype.h \ + curl_des.h curl_endian.h curl_fnmatch.h curl_get_line.h curl_gethostname.h \ + curl_gssapi.h curl_hmac.h curl_ldap.h curl_md4.h curl_md5.h curl_memory.h \ + curl_memrchr.h curl_multibyte.h curl_ntlm_core.h curl_ntlm_wb.h curl_path.h \ + curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h curl_sec.h curl_setup.h \ + curl_setup_once.h curl_sha256.h curl_sspi.h curl_threads.h curlx.h dict.h \ + dotdot.h easyif.h escape.h file.h fileinfo.h formdata.h ftp.h url.h \ + ftplistparser.h getinfo.h gopher.h hash.h hostcheck.h hostip.h http.h \ + http2.h http_chunks.h http_digest.h http_negotiate.h http_ntlm.h \ + http_proxy.h if2ip.h imap.h inet_ntop.h inet_pton.h llist.h memdebug.h \ + mime.h mqtt.h multihandle.h multiif.h netrc.h non-ascii.h nonblock.h \ + parsedate.h pingpong.h pop3.h progress.h psl.h doh.h quic.h rand.h rename.h \ + rtsp.h select.h sendf.h setopt.h setup-vms.h share.h sigpipe.h slist.h \ + smb.h smtp.h sockaddr.h socketpair.h socks.h speedcheck.h splay.h strcase.h \ + strdup.h strerror.h strtok.h strtoofft.h system_win32.h telnet.h tftp.h \ + timeval.h transfer.h urlapi-int.h urldata.h warnless.h wildcard.h \ + x509asn1.h dynbuf.h LIB_RCFILES = libcurl.rc -CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) -HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) +CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \ + $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) +HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \ + $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c index 85a4e01b500..c2ec489d9e8 100644 --- a/Utilities/cmcurl/lib/altsvc.c +++ b/Utilities/cmcurl/lib/altsvc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019, Daniel Stenberg, , et al. + * Copyright (C) 2019 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,6 +34,8 @@ #include "parsedate.h" #include "sendf.h" #include "warnless.h" +#include "rand.h" +#include "rename.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -48,15 +50,21 @@ #define MAX_ALTSVC_ALPNLENSTR "10" #define MAX_ALTSVC_ALPNLEN 10 +#if defined(USE_QUICHE) && !defined(UNITTESTS) +#define H3VERSION "h3-29" +#elif defined(USE_NGTCP2) && !defined(UNITTESTS) +#define H3VERSION "h3-29" +#else +#define H3VERSION "h3" +#endif + static enum alpnid alpn2alpnid(char *name) { if(strcasecompare(name, "h1")) return ALPN_h1; if(strcasecompare(name, "h2")) return ALPN_h2; - if(strcasecompare(name, "h2c")) - return ALPN_h2c; - if(strcasecompare(name, "h3")) + if(strcasecompare(name, H3VERSION)) return ALPN_h3; return ALPN_none; /* unknown, probably rubbish input */ } @@ -69,10 +77,8 @@ const char *Curl_alpnid2str(enum alpnid id) return "h1"; case ALPN_h2: return "h2"; - case ALPN_h2c: - return "h2c"; case ALPN_h3: - return "h3"; + return H3VERSION; default: return ""; /* bad */ } @@ -81,8 +87,8 @@ const char *Curl_alpnid2str(enum alpnid id) static void altsvc_free(struct altsvc *as) { - free(as->srchost); - free(as->dsthost); + free(as->src.host); + free(as->dst.host); free(as); } @@ -97,17 +103,17 @@ static struct altsvc *altsvc_createid(const char *srchost, if(!as) return NULL; - as->srchost = strdup(srchost); - if(!as->srchost) + as->src.host = strdup(srchost); + if(!as->src.host) goto error; - as->dsthost = strdup(dsthost); - if(!as->dsthost) + as->dst.host = strdup(dsthost); + if(!as->dst.host) goto error; - as->srcalpnid = srcalpnid; - as->dstalpnid = dstalpnid; - as->srcport = curlx_ultous(srcport); - as->dstport = curlx_ultous(dstport); + as->src.alpnid = srcalpnid; + as->dst.alpnid = dstalpnid; + as->src.port = curlx_ultous(srcport); + as->dst.port = curlx_ultous(dstport); return as; error: @@ -156,14 +162,13 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line) date, &persist, &prio); if(9 == rc) { struct altsvc *as; - time_t expires = curl_getdate(date, NULL); + time_t expires = Curl_getdate_capped(date); as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport); if(as) { as->expires = expires; as->prio = prio; as->persist = persist ? 1 : 0; Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node); - asi->num++; /* one more entry */ } } @@ -183,7 +188,16 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) { CURLcode result = CURLE_OK; char *line = NULL; - FILE *fp = fopen(file, FOPEN_READTEXT); + FILE *fp; + + /* we need a private copy of the file name so that the altsvc cache file + name survives an easy handle reset */ + free(asi->filename); + asi->filename = strdup(file); + if(!asi->filename) + return CURLE_OUT_OF_MEMORY; + + fp = fopen(file, FOPEN_READTEXT); if(fp) { line = malloc(MAX_ALTSVC_LINE); if(!line) @@ -204,6 +218,7 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) return result; fail: + Curl_safefree(asi->filename); free(line); fclose(fp); return CURLE_OUT_OF_MEMORY; @@ -226,8 +241,8 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp) "\"%d%02d%02d " "%02d:%02d:%02d\" " "%u %d\n", - Curl_alpnid2str(as->srcalpnid), as->srchost, as->srcport, - Curl_alpnid2str(as->dstalpnid), as->dsthost, as->dstport, + Curl_alpnid2str(as->src.alpnid), as->src.host, as->src.port, + Curl_alpnid2str(as->dst.alpnid), as->dst.host, as->dst.port, stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, stamp.tm_hour, stamp.tm_min, stamp.tm_sec, as->persist, as->prio); @@ -252,7 +267,7 @@ struct altsvcinfo *Curl_altsvc_init(void) #ifdef USE_NGHTTP2 | CURLALTSVC_H2 #endif -#ifdef USE_HTTP3 +#ifdef ENABLE_QUIC | CURLALTSVC_H3 #endif ; @@ -297,6 +312,7 @@ void Curl_altsvc_cleanup(struct altsvcinfo *altsvc) n = e->next; altsvc_free(as); } + free(altsvc->filename); free(altsvc); } } @@ -304,34 +320,57 @@ void Curl_altsvc_cleanup(struct altsvcinfo *altsvc) /* * Curl_altsvc_save() writes the altsvc cache to a file. */ -CURLcode Curl_altsvc_save(struct altsvcinfo *altsvc, const char *file) +CURLcode Curl_altsvc_save(struct Curl_easy *data, + struct altsvcinfo *altsvc, const char *file) { struct curl_llist_element *e; struct curl_llist_element *n; CURLcode result = CURLE_OK; FILE *out; + char *tempstore; + unsigned char randsuffix[9]; if(!altsvc) /* no cache activated */ return CURLE_OK; - if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file[0]) - /* marked as read-only or zero length file name */ + /* if not new name is given, use the one we stored from the load */ + if(!file && altsvc->filename) + file = altsvc->filename; + + if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0]) + /* marked as read-only, no file or zero length file name */ return CURLE_OK; - out = fopen(file, FOPEN_WRITETEXT); + + if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix))) + return CURLE_FAILED_INIT; + + tempstore = aprintf("%s.%s.tmp", file, randsuffix); + if(!tempstore) + return CURLE_OUT_OF_MEMORY; + + out = fopen(tempstore, FOPEN_WRITETEXT); if(!out) - return CURLE_WRITE_ERROR; - fputs("# Your alt-svc cache. https://curl.haxx.se/docs/alt-svc.html\n" - "# This file was generated by libcurl! Edit at your own risk.\n", - out); - for(e = altsvc->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; - result = altsvc_out(as, out); + result = CURLE_WRITE_ERROR; + else { + fputs("# Your alt-svc cache. https://curl.haxx.se/docs/alt-svc.html\n" + "# This file was generated by libcurl! Edit at your own risk.\n", + out); + for(e = altsvc->list.head; e; e = n) { + struct altsvc *as = e->ptr; + n = e->next; + result = altsvc_out(as, out); + if(result) + break; + } + fclose(out); + if(!result && Curl_rename(tempstore, file)) + result = CURLE_WRITE_ERROR; + if(result) - break; + unlink(tempstore); } - fclose(out); + free(tempstore); return result; } @@ -343,15 +382,15 @@ static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen) while(*p && ISBLANK(*p)) p++; protop = p; - while(*p && ISALNUM(*p)) + while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '=')) p++; len = p - protop; + *ptr = p; if(!len || (len >= buflen)) return CURLE_BAD_FUNCTION_ARGUMENT; memcpy(alpnbuf, protop, len); alpnbuf[len] = 0; - *ptr = p; return CURLE_OK; } @@ -365,12 +404,11 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid, for(e = asi->list.head; e; e = n) { struct altsvc *as = e->ptr; n = e->next; - if((srcalpnid == as->srcalpnid) && - (srcport == as->srcport) && - strcasecompare(srchost, as->srchost)) { + if((srcalpnid == as->src.alpnid) && + (srcport == as->src.port) && + strcasecompare(srchost, as->src.host)) { Curl_llist_remove(&asi->list, e, NULL); altsvc_free(as); - asi->num--; } } } @@ -391,12 +429,18 @@ static time_t debugtime(void *unused) #define time(x) debugtime(x) #endif +#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') + /* * Curl_altsvc_parse() takes an incoming alt-svc response header and stores * the data correctly in the cache. * * 'value' points to the header *value*. That's contents to the right of the * header name. + * + * Currently this function rejects invalid data without returning an error. + * Invalid host name, port number will result in the specific alternative + * being rejected. Unknown protocols are skipped. */ CURLcode Curl_altsvc_parse(struct Curl_easy *data, struct altsvcinfo *asi, const char *value, @@ -410,12 +454,11 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, char alpnbuf[MAX_ALTSVC_ALPNLEN] = ""; struct altsvc *as; unsigned short dstport = srcport; /* the same by default */ - const char *semip; - time_t maxage = 24 * 3600; /* default is 24 hours */ - bool persist = FALSE; CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf)); - if(result) - return result; + if(result) { + infof(data, "Excessive alt-svc header, ignoring...\n"); + return CURLE_OK; + } DEBUGASSERT(asi); @@ -427,47 +470,20 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, return CURLE_OK; } - /* The 'ma' and 'persist' flags are annoyingly meant for all alternatives - but are set after the list on the line. Scan for the semicolons and get - those fields first! */ - semip = p; - do { - semip = strchr(semip, ';'); - if(semip) { - char option[32]; - unsigned long num; - char *end_ptr; - semip++; /* pass the semicolon */ - result = getalnum(&semip, option, sizeof(option)); - if(result) - break; - while(*semip && ISBLANK(*semip)) - semip++; - if(*semip != '=') - continue; - semip++; - num = strtoul(semip, &end_ptr, 10); - if(num < ULONG_MAX) { - if(strcasecompare("ma", option)) - maxage = num; - else if(strcasecompare("persist", option) && (num == 1)) - persist = TRUE; - } - semip = end_ptr; - } - } while(semip); - do { if(*p == '=') { /* [protocol]="[host][:port]" */ dstalpnid = alpn2alpnid(alpnbuf); - if(!dstalpnid) { - infof(data, "Unknown alt-svc protocol \"%s\", ignoring...\n", alpnbuf); - return CURLE_OK; - } p++; if(*p == '\"') { - const char *dsthost; + const char *dsthost = ""; + const char *value_ptr; + char option[32]; + unsigned long num; + char *end_ptr; + bool quoted = FALSE; + time_t maxage = 24 * 3600; /* default is 24 hours */ + bool persist = FALSE; p++; if(*p != ':') { /* host name starts here */ @@ -475,11 +491,15 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) p++; len = p - hostp; - if(!len || (len >= MAX_ALTSVC_HOSTLEN)) - return CURLE_BAD_FUNCTION_ARGUMENT; - memcpy(namebuf, hostp, len); - namebuf[len] = 0; - dsthost = namebuf; + if(!len || (len >= MAX_ALTSVC_HOSTLEN)) { + infof(data, "Excessive alt-svc host name, ignoring...\n"); + dstalpnid = ALPN_none; + } + else { + memcpy(namebuf, hostp, len); + namebuf[len] = 0; + dsthost = namebuf; + } } else { /* no destination name, use source host */ @@ -487,31 +507,85 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, } if(*p == ':') { /* a port number */ - char *end_ptr; unsigned long port = strtoul(++p, &end_ptr, 10); if(port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') { infof(data, "Unknown alt-svc port number, ignoring...\n"); - return CURLE_OK; + dstalpnid = ALPN_none; } p = end_ptr; dstport = curlx_ultous(port); } if(*p++ != '\"') - return CURLE_BAD_FUNCTION_ARGUMENT; - as = altsvc_createid(srchost, dsthost, - srcalpnid, dstalpnid, - srcport, dstport); - if(as) { - /* The expires time also needs to take the Age: value (if any) into - account. [See RFC 7838 section 3.1] */ - as->expires = maxage + time(NULL); - as->persist = persist; - Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node); - asi->num++; /* one more entry */ - infof(data, "Added alt-svc: %s:%d over %s\n", dsthost, dstport, - Curl_alpnid2str(dstalpnid)); + break; + /* Handle the optional 'ma' and 'persist' flags. Unknown flags + are skipped. */ + for(;;) { + while(ISBLANK(*p)) + p++; + if(*p != ';') + break; + p++; /* pass the semicolon */ + if(!*p || ISNEWLINE(*p)) + break; + result = getalnum(&p, option, sizeof(option)); + if(result) { + /* skip option if name is too long */ + option[0] = '\0'; + } + while(*p && ISBLANK(*p)) + p++; + if(*p != '=') + return CURLE_OK; + p++; + while(*p && ISBLANK(*p)) + p++; + if(!*p) + return CURLE_OK; + if(*p == '\"') { + /* quoted value */ + p++; + quoted = TRUE; + } + value_ptr = p; + if(quoted) { + while(*p && *p != '\"') + p++; + if(!*p++) + return CURLE_OK; + } + else { + while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',') + p++; + } + num = strtoul(value_ptr, &end_ptr, 10); + if((end_ptr != value_ptr) && (num < ULONG_MAX)) { + if(strcasecompare("ma", option)) + maxage = num; + else if(strcasecompare("persist", option) && (num == 1)) + persist = TRUE; + } + } + if(dstalpnid) { + as = altsvc_createid(srchost, dsthost, + srcalpnid, dstalpnid, + srcport, dstport); + if(as) { + /* The expires time also needs to take the Age: value (if any) into + account. [See RFC 7838 section 3.1] */ + as->expires = maxage + time(NULL); + as->persist = persist; + Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node); + infof(data, "Added alt-svc: %s:%d over %s\n", dsthost, dstport, + Curl_alpnid2str(dstalpnid)); + } + } + else { + infof(data, "Unknown alt-svc protocol \"%s\", skipping...\n", + alpnbuf); } } + else + break; /* after the double quote there can be a comma if there's another string or a semicolon if no more */ if(*p == ',') { @@ -519,11 +593,11 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, p++; result = getalnum(&p, alpnbuf, sizeof(alpnbuf)); if(result) - /* failed to parse, but since we already did at least one host we - return OK */ - return CURLE_OK; + break; } } + else + break; } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r')); return CURLE_OK; @@ -535,31 +609,31 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, bool Curl_altsvc_lookup(struct altsvcinfo *asi, enum alpnid srcalpnid, const char *srchost, int srcport, - enum alpnid *dstalpnid, const char **dsthost, - int *dstport) + struct altsvc **dstentry, + const int versions) /* one or more bits */ { struct curl_llist_element *e; struct curl_llist_element *n; time_t now = time(NULL); DEBUGASSERT(asi); DEBUGASSERT(srchost); - DEBUGASSERT(dsthost); + DEBUGASSERT(dstentry); for(e = asi->list.head; e; e = n) { struct altsvc *as = e->ptr; n = e->next; if(as->expires < now) { /* an expired entry, remove */ + Curl_llist_remove(&asi->list, e, NULL); altsvc_free(as); continue; } - if((as->srcalpnid == srcalpnid) && - strcasecompare(as->srchost, srchost) && - as->srcport == srcport) { + if((as->src.alpnid == srcalpnid) && + strcasecompare(as->src.host, srchost) && + (as->src.port == srcport) && + (versions & as->dst.alpnid)) { /* match */ - *dstalpnid = as->dstalpnid; - *dsthost = as->dsthost; - *dstport = as->dstport; + *dstentry = as; return TRUE; } } diff --git a/Utilities/cmcurl/lib/altsvc.h b/Utilities/cmcurl/lib/altsvc.h index eefb45bf613..578a4fbfb07 100644 --- a/Utilities/cmcurl/lib/altsvc.h +++ b/Utilities/cmcurl/lib/altsvc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019, Daniel Stenberg, , et al. + * Copyright (C) 2019 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,20 +28,21 @@ #include "llist.h" enum alpnid { - ALPN_none, - ALPN_h1, - ALPN_h2, - ALPN_h2c, - ALPN_h3 + ALPN_none = 0, + ALPN_h1 = CURLALTSVC_H1, + ALPN_h2 = CURLALTSVC_H2, + ALPN_h3 = CURLALTSVC_H3 +}; + +struct althost { + char *host; + unsigned short port; + enum alpnid alpnid; }; struct altsvc { - char *srchost; - char *dsthost; - unsigned short srcport; - unsigned short dstport; - enum alpnid srcalpnid; - enum alpnid dstalpnid; + struct althost src; + struct althost dst; time_t expires; bool persist; int prio; @@ -51,14 +52,14 @@ struct altsvc { struct altsvcinfo { char *filename; struct curl_llist list; /* list of entries */ - size_t num; /* number of alt-svc entries */ long flags; /* the publicly set bitmask */ }; const char *Curl_alpnid2str(enum alpnid id); struct altsvcinfo *Curl_altsvc_init(void); CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file); -CURLcode Curl_altsvc_save(struct altsvcinfo *asi, const char *file); +CURLcode Curl_altsvc_save(struct Curl_easy *data, + struct altsvcinfo *asi, const char *file); CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl); void Curl_altsvc_cleanup(struct altsvcinfo *altsvc); CURLcode Curl_altsvc_parse(struct Curl_easy *data, @@ -68,10 +69,10 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, bool Curl_altsvc_lookup(struct altsvcinfo *asi, enum alpnid srcalpnid, const char *srchost, int srcport, - enum alpnid *dstalpnid, const char **dsthost, - int *dstport); + struct altsvc **dstentry, + const int versions); /* CURLALTSVC_H* bits */ #else /* disabled */ -#define Curl_altsvc_save(a,b) +#define Curl_altsvc_save(a,b,c) #endif /* CURL_DISABLE_HTTP || USE_ALTSVC */ #endif /* HEADER_CURL_ALTSVC_H */ diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index 8561a472464..ba5160b253e 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -87,7 +87,8 @@ struct ResolverResults { int num_pending; /* number of ares_gethostbyname() requests */ - Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */ + struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares + parts */ int last_status; struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ }; @@ -253,16 +254,14 @@ static void destroy_async_data(struct Curl_async *async) */ int Curl_resolver_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) - + curl_socket_t *socks) { struct timeval maxtime; struct timeval timebuf; struct timeval *timeout; long milli; int max = ares_getsock((ares_channel)conn->data->state.resolver, - (ares_socket_t *)socks, numsocks); + (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; maxtime.tv_usec = 0; @@ -287,7 +286,7 @@ int Curl_resolver_getsock(struct connectdata *conn, * return number of sockets it worked on */ -static int waitperform(struct connectdata *conn, int timeout_ms) +static int waitperform(struct connectdata *conn, timediff_t timeout_ms) { struct Curl_easy *data = conn->data; int nfds; @@ -354,8 +353,8 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, conn->async.os_specific; CURLcode result = CURLE_OK; - if(dns) - *dns = NULL; + DEBUGASSERT(dns); + *dns = NULL; waitperform(conn, 0); @@ -383,19 +382,18 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, } if(res && !res->num_pending) { - if(dns) { - (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai); - /* temp_ai ownership is moved to the connection, so we need not free-up - them */ - res->temp_ai = NULL; - } + (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai); + /* temp_ai ownership is moved to the connection, so we need not free-up + them */ + res->temp_ai = NULL; + if(!conn->async.dns) { failf(data, "Could not resolve: %s (%s)", conn->async.hostname, ares_strerror(conn->async.status)); result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } - else if(dns) + else *dns = conn->async.dns; destroy_async_data(&conn->async); @@ -410,7 +408,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, * 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 + * 'entry' 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. @@ -422,10 +420,9 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, struct Curl_easy *data = conn->data; timediff_t timeout; struct curltime now = Curl_now(); - struct Curl_dns_entry *temp_entry; - if(entry) - *entry = NULL; /* clear on entry */ + DEBUGASSERT(entry); + *entry = NULL; /* clear on entry */ timeout = Curl_timeleft(data, &now, TRUE); if(timeout < 0) { @@ -440,9 +437,13 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, while(!result) { struct timeval *tvp, tv, store; int itimeout; - int timeout_ms; + timediff_t timeout_ms; - itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout; +#if TIMEDIFF_T_MAX > INT_MAX + itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout; +#else + itimeout = (int)timeout; +#endif store.tv_sec = itimeout/1000; store.tv_usec = (itimeout%1000)*1000; @@ -453,12 +454,12 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, second is left, otherwise just use 1000ms to make sure the progress callback gets called frequent enough */ if(!tvp->tv_sec) - timeout_ms = (int)(tvp->tv_usec/1000); + timeout_ms = (timediff_t)(tvp->tv_usec/1000); else timeout_ms = 1000; waitperform(conn, timeout_ms); - result = Curl_resolver_is_resolved(conn, entry?&temp_entry:NULL); + result = Curl_resolver_is_resolved(conn, entry); if(result || conn->async.done) break; @@ -473,7 +474,7 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, else if(timediff > timeout) timeout = -1; else - timeout -= (long)timediff; + timeout -= timediff; now = now2; /* for next loop */ } if(timeout < 0) @@ -498,9 +499,9 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, /* Connects results to the list */ static void compound_results(struct ResolverResults *res, - Curl_addrinfo *ai) + struct Curl_addrinfo *ai) { - Curl_addrinfo *ai_tail; + struct Curl_addrinfo *ai_tail; if(!ai) return; ai_tail = ai; @@ -542,7 +543,7 @@ static void query_completed_cb(void *arg, /* (struct connectdata *) */ res->num_pending--; if(CURL_ASYNC_SUCCESS == status) { - Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port); + struct Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port); if(ai) { compound_results(res, ai); } @@ -621,33 +622,18 @@ static void query_completed_cb(void *arg, /* (struct connectdata *) */ * memory we need to free after use. That memory *MUST* be freed with * Curl_freeaddrinfo(), nothing else. */ -Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, - const char *hostname, - int port, - int *waitp) +struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, + const char *hostname, + int port, + int *waitp) { char *bufp; struct Curl_easy *data = conn->data; - struct in_addr in; int family = PF_INET; -#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ - struct in6_addr in6; -#endif /* CURLRES_IPV6 */ *waitp = 0; /* default to synchronous response */ - /* First check if this is an IPv4 address string */ - if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { - /* This is a dotted IP address 123.123.123.123-style */ - return Curl_ip2addr(AF_INET, &in, hostname, port); - } - #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ - /* Otherwise, check if this is an IPv6 address string */ - if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0) - /* This must be an IPv6 address literal. */ - return Curl_ip2addr(AF_INET6, &in6, hostname, port); - switch(conn->ip_version) { default: #if ARES_VERSION >= 0x010601 @@ -686,7 +672,7 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, res->last_status = ARES_ENOTFOUND; #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ if(family == PF_UNSPEC) { - if(Curl_ipv6works()) { + if(Curl_ipv6works(conn)) { res->num_pending = 2; /* areschannel is already setup in the Curl_open() function */ @@ -734,7 +720,11 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data, return CURLE_OK; #if (ARES_VERSION >= 0x010704) +#if (ARES_VERSION >= 0x010b00) + ares_result = ares_set_servers_ports_csv(data->state.resolver, servers); +#else ares_result = ares_set_servers_csv(data->state.resolver, servers); +#endif switch(ares_result) { case ARES_SUCCESS: result = CURLE_OK; diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c index 55e0811c5cd..a60f4f0661d 100644 --- a/Utilities/cmcurl/lib/asyn-thread.c +++ b/Utilities/cmcurl/lib/asyn-thread.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -21,6 +21,7 @@ ***************************************************************************/ #include "curl_setup.h" +#include "socketpair.h" /*********************************************************************** * Only for threaded name resolves builds @@ -70,10 +71,10 @@ #include "strerror.h" #include "url.h" #include "multiif.h" -#include "inet_pton.h" #include "inet_ntop.h" #include "curl_threads.h" #include "connect.h" +#include "socketpair.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -157,14 +158,18 @@ static bool init_resolve_thread(struct connectdata *conn, /* Data for synchronization between resolver thread and its parent */ struct thread_sync_data { - curl_mutex_t * mtx; + curl_mutex_t *mtx; int done; char *hostname; /* hostname to resolve, Curl_async.hostname duplicate */ int port; +#ifdef USE_SOCKETPAIR + struct connectdata *conn; + curl_socket_t sock_pair[2]; /* socket pair */ +#endif int sock_error; - Curl_addrinfo *res; + struct Curl_addrinfo *res; #ifdef HAVE_GETADDRINFO struct addrinfo hints; #endif @@ -174,7 +179,7 @@ struct thread_sync_data { struct thread_data { curl_thread_t thread_hnd; unsigned int poll_interval; - time_t interval_end; + timediff_t interval_end; struct thread_sync_data tsd; }; @@ -185,7 +190,7 @@ static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn) /* Destroy resolver thread synchronization data */ static -void destroy_thread_sync_data(struct thread_sync_data * tsd) +void destroy_thread_sync_data(struct thread_sync_data *tsd) { if(tsd->mtx) { Curl_mutex_destroy(tsd->mtx); @@ -197,12 +202,21 @@ void destroy_thread_sync_data(struct thread_sync_data * tsd) if(tsd->res) Curl_freeaddrinfo(tsd->res); +#ifdef USE_SOCKETPAIR + /* + * close one end of the socket pair (may be done in resolver thread); + * the other end (for reading) is always closed in the parent thread. + */ + if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { + sclose(tsd->sock_pair[1]); + } +#endif memset(tsd, 0, sizeof(*tsd)); } /* Initialize resolver thread synchronization data */ static -int init_thread_sync_data(struct thread_data * td, +int init_thread_sync_data(struct thread_data *td, const char *hostname, int port, const struct addrinfo *hints) @@ -230,6 +244,14 @@ int init_thread_sync_data(struct thread_data * td, Curl_mutex_init(tsd->mtx); +#ifdef USE_SOCKETPAIR + /* create socket pair, avoid AF_LOCAL since it doesn't build on Solaris */ + if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 0) { + tsd->sock_pair[0] = CURL_SOCKET_BAD; + tsd->sock_pair[1] = CURL_SOCKET_BAD; + goto err_exit; + } +#endif tsd->sock_error = CURL_ASYNC_SUCCESS; /* Copying hostname string because original can be destroyed by parent @@ -276,6 +298,9 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) struct thread_data *td = tsd->td; char service[12]; int rc; +#ifdef USE_SOCKETPAIR + char buf[1]; +#endif msnprintf(service, sizeof(service), "%d", tsd->port); @@ -298,6 +323,16 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) free(td); } else { +#ifdef USE_SOCKETPAIR + if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { + /* DNS has been resolved, signal client task */ + buf[0] = 1; + if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { + /* update sock_erro to errno */ + tsd->sock_error = SOCKERRNO; + } + } +#endif tsd->done = 1; Curl_mutex_release(tsd->mtx); } @@ -348,6 +383,10 @@ static void destroy_async_data(struct Curl_async *async) if(async->os_specific) { struct thread_data *td = (struct thread_data*) async->os_specific; int done; +#ifdef USE_SOCKETPAIR + curl_socket_t sock_rd = td->tsd.sock_pair[0]; + struct connectdata *conn = td->tsd.conn; +#endif /* * if the thread is still blocking in the resolve syscall, detach it and @@ -369,6 +408,15 @@ static void destroy_async_data(struct Curl_async *async) free(async->os_specific); } +#ifdef USE_SOCKETPAIR + /* + * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE + * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL + */ + if(conn) + Curl_multi_closed(conn->data, sock_rd); + sclose(sock_rd); +#endif } async->os_specific = NULL; @@ -446,11 +494,14 @@ static CURLcode resolver_error(struct connectdata *conn) const char *host_or_proxy; CURLcode result; +#ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy) { host_or_proxy = "proxy"; result = CURLE_COULDNT_RESOLVE_PROXY; } - else { + else +#endif + { host_or_proxy = "host"; result = CURLE_COULDNT_RESOLVE_HOST; } @@ -461,6 +512,9 @@ static CURLcode resolver_error(struct connectdata *conn) return result; } +/* + * 'entry' may be NULL and then no data is returned + */ static CURLcode thread_wait_resolv(struct connectdata *conn, struct Curl_dns_entry **entry, bool report) @@ -545,6 +599,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, struct thread_data *td = (struct thread_data*) conn->async.os_specific; int done = 0; + DEBUGASSERT(entry); *entry = NULL; if(!td) { @@ -569,6 +624,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, } 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 = Curl_timediff(Curl_now(), data->progress.t_startsingle); if(elapsed < 0) @@ -592,47 +648,61 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, } int Curl_resolver_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { - time_t milli; + int ret_val = 0; + timediff_t milli; timediff_t ms; struct Curl_easy *data = conn->data; struct resdata *reslv = (struct resdata *)data->state.resolver; +#ifdef USE_SOCKETPAIR + struct thread_data *td = (struct thread_data*)conn->async.os_specific; +#else (void)socks; - (void)numsocks; - ms = Curl_timediff(Curl_now(), reslv->start); - if(ms < 3) - milli = 0; - else if(ms <= 50) - milli = ms/3; - else if(ms <= 250) - milli = 50; - else - milli = 200; - Curl_expire(data, milli, EXPIRE_ASYNC_NAME); - return 0; +#endif + +#ifdef USE_SOCKETPAIR + if(td) { + /* return read fd to client for polling the DNS resolution status */ + socks[0] = td->tsd.sock_pair[0]; + DEBUGASSERT(td->tsd.conn == conn || !td->tsd.conn); + td->tsd.conn = conn; + ret_val = GETSOCK_READSOCK(0); + } + else { +#endif + ms = Curl_timediff(Curl_now(), reslv->start); + if(ms < 3) + milli = 0; + else if(ms <= 50) + milli = ms/3; + else if(ms <= 250) + milli = 50; + else + milli = 200; + Curl_expire(data, milli, EXPIRE_ASYNC_NAME); +#ifdef USE_SOCKETPAIR + } +#endif + + + return ret_val; } #ifndef HAVE_GETADDRINFO /* * Curl_getaddrinfo() - for platforms without getaddrinfo */ -Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, - const char *hostname, - int port, - int *waitp) +struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, + const char *hostname, + int port, + int *waitp) { - struct in_addr in; struct Curl_easy *data = conn->data; struct resdata *reslv = (struct resdata *)data->state.resolver; *waitp = 0; /* default to synchronous response */ - if(Curl_inet_pton(AF_INET, hostname, &in) > 0) - /* This is a dotted IP address 123.123.123.123-style */ - return Curl_ip2addr(AF_INET, &in, hostname, port); - reslv->start = Curl_now(); /* fire up a new resolver thread! */ @@ -651,38 +721,18 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, /* * Curl_resolver_getaddrinfo() - for getaddrinfo */ -Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, - const char *hostname, - int port, - int *waitp) +struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, + const char *hostname, + int port, + int *waitp) { struct addrinfo hints; - char sbuf[12]; int pf = PF_INET; struct Curl_easy *data = conn->data; struct resdata *reslv = (struct resdata *)data->state.resolver; *waitp = 0; /* default to synchronous response */ -#ifndef USE_RESOLVE_ON_IPS - { - struct in_addr in; - /* First check if this is an IPv4 address string */ - if(Curl_inet_pton(AF_INET, hostname, &in) > 0) - /* This is a dotted IP address 123.123.123.123-style */ - return Curl_ip2addr(AF_INET, &in, hostname, port); - } -#ifdef CURLRES_IPV6 - { - struct in6_addr in6; - /* check if this is an IPv6 address string */ - if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) - /* This is an IPv6 address literal */ - return Curl_ip2addr(AF_INET6, &in6, hostname, port); - } -#endif /* CURLRES_IPV6 */ -#endif /* !USE_RESOLVE_ON_IPS */ - #ifdef CURLRES_IPV6 /* * Check if a limited name resolve has been requested. @@ -699,16 +749,15 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, break; } - if((pf != PF_INET) && !Curl_ipv6works()) + if((pf != PF_INET) && !Curl_ipv6works(conn)) /* The stack seems to be a non-IPv6 one */ pf = PF_INET; #endif /* CURLRES_IPV6 */ memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = conn->socktype; - - msnprintf(sbuf, sizeof(sbuf), "%d", port); + hints.ai_socktype = (conn->transport == TRNSPRT_TCP)? + SOCK_STREAM : SOCK_DGRAM; reslv->start = Curl_now(); /* fire up a new resolver thread! */ diff --git a/Utilities/cmcurl/lib/asyn.h b/Utilities/cmcurl/lib/asyn.h index ccd4b1f7e2d..be2796cf0f6 100644 --- a/Utilities/cmcurl/lib/asyn.h +++ b/Utilities/cmcurl/lib/asyn.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -114,8 +114,7 @@ void Curl_resolver_kill(struct connectdata *conn); * return bitmask indicating what file descriptors (referring to array indexes * in the 'sock' array) to wait for, read/write. */ -int Curl_resolver_getsock(struct connectdata *conn, curl_socket_t *sock, - int numsocks); +int Curl_resolver_getsock(struct connectdata *conn, curl_socket_t *sock); /* * Curl_resolver_is_resolved() @@ -154,10 +153,10 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, * Each resolver backend must of course make sure to return data in the * correct format to comply with this. */ -Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, - const char *hostname, - int port, - int *waitp); +struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, + const char *hostname, + int port, + int *waitp); #ifndef CURLRES_ASYNCH /* convert these functions if an asynch resolver isn't used */ diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c index fb081a6bb85..643cef6251b 100644 --- a/Utilities/cmcurl/lib/base64.c +++ b/Utilities/cmcurl/lib/base64.c @@ -24,8 +24,8 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_LIBSSH2) || \ - defined(USE_LIBSSH) || !defined(CURL_DISABLE_LDAP) || \ +#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \ + !defined(CURL_DISABLE_LDAP) || \ !defined(CURL_DISABLE_DOH) || defined(USE_SSL) #include "urldata.h" /* for the Curl_easy definition */ diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index 53509199650..d21a00cfdd9 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, - * Copyright (C) 2012 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,26 +40,7 @@ #include "curl_memory.h" #include "memdebug.h" -#ifdef CURLDEBUG -/* the debug versions of these macros make extra certain that the lock is - never doubly locked or unlocked */ -#define CONN_LOCK(x) if((x)->share) { \ - Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE); \ - DEBUGASSERT(!(x)->state.conncache_lock); \ - (x)->state.conncache_lock = TRUE; \ - } - -#define CONN_UNLOCK(x) if((x)->share) { \ - DEBUGASSERT((x)->state.conncache_lock); \ - (x)->state.conncache_lock = FALSE; \ - Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT); \ - } -#else -#define CONN_LOCK(x) if((x)->share) \ - Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE) -#define CONN_UNLOCK(x) if((x)->share) \ - Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT) -#endif +#define HASHKEY_SIZE 128 static void conn_llist_dtor(void *user, void *element) { @@ -68,58 +49,57 @@ static void conn_llist_dtor(void *user, void *element) conn->bundle = NULL; } -static CURLcode bundle_create(struct Curl_easy *data, - struct connectbundle **cb_ptr) +static CURLcode bundle_create(struct connectbundle **bundlep) { - (void)data; - DEBUGASSERT(*cb_ptr == NULL); - *cb_ptr = malloc(sizeof(struct connectbundle)); - if(!*cb_ptr) + DEBUGASSERT(*bundlep == NULL); + *bundlep = malloc(sizeof(struct connectbundle)); + if(!*bundlep) return CURLE_OUT_OF_MEMORY; - (*cb_ptr)->num_connections = 0; - (*cb_ptr)->multiuse = BUNDLE_UNKNOWN; + (*bundlep)->num_connections = 0; + (*bundlep)->multiuse = BUNDLE_UNKNOWN; - Curl_llist_init(&(*cb_ptr)->conn_list, (curl_llist_dtor) conn_llist_dtor); + Curl_llist_init(&(*bundlep)->conn_list, (curl_llist_dtor) conn_llist_dtor); return CURLE_OK; } -static void bundle_destroy(struct connectbundle *cb_ptr) +static void bundle_destroy(struct connectbundle *bundle) { - if(!cb_ptr) + if(!bundle) return; - Curl_llist_destroy(&cb_ptr->conn_list, NULL); + Curl_llist_destroy(&bundle->conn_list, NULL); - free(cb_ptr); + free(bundle); } /* Add a connection to a bundle */ -static void bundle_add_conn(struct connectbundle *cb_ptr, +static void bundle_add_conn(struct connectbundle *bundle, struct connectdata *conn) { - Curl_llist_insert_next(&cb_ptr->conn_list, cb_ptr->conn_list.tail, conn, + Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn, &conn->bundle_node); - conn->bundle = cb_ptr; - cb_ptr->num_connections++; + conn->bundle = bundle; + bundle->num_connections++; } /* Remove a connection from a bundle */ -static int bundle_remove_conn(struct connectbundle *cb_ptr, +static int bundle_remove_conn(struct connectbundle *bundle, struct connectdata *conn) { struct curl_llist_element *curr; - curr = cb_ptr->conn_list.head; + curr = bundle->conn_list.head; while(curr) { if(curr->ptr == conn) { - Curl_llist_remove(&cb_ptr->conn_list, curr, NULL); - cb_ptr->num_connections--; + Curl_llist_remove(&bundle->conn_list, curr, NULL); + bundle->num_connections--; conn->bundle = NULL; return 1; /* we removed a handle */ } curr = curr->next; } + DEBUGASSERT(0); return 0; } @@ -141,10 +121,8 @@ int Curl_conncache_init(struct conncache *connc, int size) rc = Curl_hash_init(&connc->hash, size, Curl_hash_str, Curl_str_key_compare, free_bundle_hash_entry); - if(rc) { - Curl_close(connc->closure_handle); - connc->closure_handle = NULL; - } + if(rc) + Curl_close(&connc->closure_handle); else connc->closure_handle->state.conn_cache = connc; @@ -159,28 +137,30 @@ void Curl_conncache_destroy(struct conncache *connc) /* creates a key to find a bundle for this connection */ static void hashkey(struct connectdata *conn, char *buf, - size_t len) /* something like 128 is fine */ + size_t len, /* something like 128 is fine */ + const char **hostp) { const char *hostname; + long port = conn->remote_port; - if(conn->bits.socksproxy) - hostname = conn->socks_proxy.host.name; - else if(conn->bits.httpproxy) +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { hostname = conn->http_proxy.host.name; - else if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; + port = conn->port; + } + else +#endif + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; else hostname = conn->host.name; - DEBUGASSERT(len > 32); + if(hostp) + /* report back which name we used */ + *hostp = hostname; /* put the number first so that the hostname gets cut off if too long */ - msnprintf(buf, len, "%ld%s", conn->port, hostname); -} - -void Curl_conncache_unlock(struct Curl_easy *data) -{ - CONN_UNLOCK(data); + msnprintf(buf, len, "%ld%s", port, hostname); } /* Returns number of connections currently held in the connection cache. @@ -189,21 +169,9 @@ void Curl_conncache_unlock(struct Curl_easy *data) size_t Curl_conncache_size(struct Curl_easy *data) { size_t num; - CONN_LOCK(data); + CONNCACHE_LOCK(data); num = data->state.conn_cache->num_conn; - CONN_UNLOCK(data); - return num; -} - -/* Returns number of connections currently held in the connections's bundle - Locks/unlocks the cache itself! -*/ -size_t Curl_conncache_bundle_size(struct connectdata *conn) -{ - size_t num; - CONN_LOCK(conn->data); - num = conn->bundle->num_connections; - CONN_UNLOCK(conn->data); + CONNCACHE_UNLOCK(data); return num; } @@ -212,13 +180,14 @@ size_t Curl_conncache_bundle_size(struct connectdata *conn) **NOTE**: When it returns, it holds the connection cache lock! */ struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn, - struct conncache *connc) + struct conncache *connc, + const char **hostp) { struct connectbundle *bundle = NULL; - CONN_LOCK(conn->data); + CONNCACHE_LOCK(conn->data); if(connc) { - char key[128]; - hashkey(conn, key, sizeof(key)); + char key[HASHKEY_SIZE]; + hashkey(conn, key, sizeof(key), hostp); bundle = Curl_hash_pick(&connc->hash, key, strlen(key)); } @@ -262,30 +231,28 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, struct connectdata *conn) { CURLcode result = CURLE_OK; - struct connectbundle *bundle; - struct connectbundle *new_bundle = NULL; + struct connectbundle *bundle = NULL; struct Curl_easy *data = conn->data; /* *find_bundle() locks the connection cache */ - bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache); + bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache, NULL); if(!bundle) { int rc; - char key[128]; + char key[HASHKEY_SIZE]; - result = bundle_create(data, &new_bundle); + result = bundle_create(&bundle); if(result) { goto unlock; } - hashkey(conn, key, sizeof(key)); - rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle); + hashkey(conn, key, sizeof(key), NULL); + rc = conncache_add_bundle(data->state.conn_cache, key, bundle); if(!rc) { - bundle_destroy(new_bundle); + bundle_destroy(bundle); result = CURLE_OUT_OF_MEMORY; goto unlock; } - bundle = new_bundle; } bundle_add_conn(bundle, conn); @@ -297,15 +264,17 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, conn->connection_id, connc->num_conn)); unlock: - CONN_UNLOCK(data); + CONNCACHE_UNLOCK(data); return result; } /* - * Removes the connectdata object from the connection cache *and* clears the - * ->data pointer association. Pass TRUE/FALSE in the 'lock' argument - * depending on if the parent function already holds the lock or not. + * Removes the connectdata object from the connection cache, but does *not* + * clear the conn->data association. The transfer still owns this connection. + * + * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function + * already holds the lock or not. */ void Curl_conncache_remove_conn(struct Curl_easy *data, struct connectdata *conn, bool lock) @@ -317,7 +286,7 @@ void Curl_conncache_remove_conn(struct Curl_easy *data, due to a failed connection attempt, before being added to a bundle */ if(bundle) { if(lock) { - CONN_LOCK(data); + CONNCACHE_LOCK(data); } bundle_remove_conn(bundle, conn); if(bundle->num_connections == 0) @@ -328,9 +297,8 @@ void Curl_conncache_remove_conn(struct Curl_easy *data, DEBUGF(infof(data, "The cache now contains %zu members\n", connc->num_conn)); } - conn->data = NULL; /* clear the association */ if(lock) { - CONN_UNLOCK(data); + CONNCACHE_UNLOCK(data); } } } @@ -359,7 +327,7 @@ bool Curl_conncache_foreach(struct Curl_easy *data, if(!connc) return FALSE; - CONN_LOCK(data); + CONNCACHE_LOCK(data); Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); @@ -377,12 +345,12 @@ bool Curl_conncache_foreach(struct Curl_easy *data, curr = curr->next; if(1 == func(conn, param)) { - CONN_UNLOCK(data); + CONNCACHE_UNLOCK(data); return TRUE; } } } - CONN_UNLOCK(data); + CONNCACHE_UNLOCK(data); return FALSE; } @@ -423,17 +391,15 @@ conncache_find_first_connection(struct conncache *connc) * * Return TRUE if stored, FALSE if closed. */ -bool Curl_conncache_return_conn(struct connectdata *conn) +bool Curl_conncache_return_conn(struct Curl_easy *data, + struct connectdata *conn) { - struct Curl_easy *data = conn->data; - /* data->multi->maxconnects can be negative, deal with it. */ size_t maxconnects = (data->multi->maxconnects < 0) ? data->multi->num_easy * 4: data->multi->maxconnects; struct connectdata *conn_candidate = NULL; - conn->data = NULL; /* no owner anymore */ conn->lastused = Curl_now(); /* it was used up until now */ if(maxconnects > 0 && Curl_conncache_size(data) > maxconnects) { @@ -523,7 +489,7 @@ Curl_conncache_extract_oldest(struct Curl_easy *data) now = Curl_now(); - CONN_LOCK(data); + CONNCACHE_LOCK(data); Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); @@ -536,7 +502,8 @@ Curl_conncache_extract_oldest(struct Curl_easy *data) while(curr) { conn = curr->ptr; - if(!CONN_INUSE(conn) && !conn->data) { + if(!CONN_INUSE(conn) && !conn->data && !conn->bits.close && + !conn->bits.connect_only) { /* Set higher score for the age passed since the connection was used */ score = Curl_timediff(now, conn->lastused); @@ -559,7 +526,7 @@ Curl_conncache_extract_oldest(struct Curl_easy *data) connc->num_conn)); conn_candidate->data = data; /* associate! */ } - CONN_UNLOCK(data); + CONNCACHE_UNLOCK(data); return conn_candidate; } @@ -567,6 +534,11 @@ Curl_conncache_extract_oldest(struct Curl_easy *data) void Curl_conncache_close_all_connections(struct conncache *connc) { struct connectdata *conn; + char buffer[READBUFFER_MIN + 1]; + if(!connc->closure_handle) + return; + connc->closure_handle->state.buffer = buffer; + connc->closure_handle->set.buffer_size = READBUFFER_MIN; conn = conncache_find_first_connection(connc); while(conn) { @@ -576,19 +548,21 @@ void Curl_conncache_close_all_connections(struct conncache *connc) sigpipe_ignore(conn->data, &pipe_st); /* This will remove the connection from the cache */ connclose(conn, "kill all"); + Curl_conncache_remove_conn(conn->data, conn, TRUE); (void)Curl_disconnect(connc->closure_handle, conn, FALSE); sigpipe_restore(&pipe_st); conn = conncache_find_first_connection(connc); } + connc->closure_handle->state.buffer = NULL; if(connc->closure_handle) { SIGPIPE_VARIABLE(pipe_st); sigpipe_ignore(connc->closure_handle, &pipe_st); Curl_hostcache_clean(connc->closure_handle, connc->closure_handle->dns.hostcache); - Curl_close(connc->closure_handle); + Curl_close(&connc->closure_handle); sigpipe_restore(&pipe_st); } } diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h index 35be9e0aa1c..3dda21cdc66 100644 --- a/Utilities/cmcurl/lib/conncache.h +++ b/Utilities/cmcurl/lib/conncache.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2015 - 2020, Daniel Stenberg, , et al. * Copyright (C) 2012 - 2014, Linus Nielsen Feltzing, * * This software is licensed as described in the file COPYING, which @@ -42,6 +42,27 @@ struct conncache { #define BUNDLE_UNKNOWN 0 /* initial value */ #define BUNDLE_MULTIPLEX 2 +#ifdef CURLDEBUG +/* the debug versions of these macros make extra certain that the lock is + never doubly locked or unlocked */ +#define CONNCACHE_LOCK(x) if((x)->share) { \ + Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE); \ + DEBUGASSERT(!(x)->state.conncache_lock); \ + (x)->state.conncache_lock = TRUE; \ + } + +#define CONNCACHE_UNLOCK(x) if((x)->share) { \ + DEBUGASSERT((x)->state.conncache_lock); \ + (x)->state.conncache_lock = FALSE; \ + Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT); \ + } +#else +#define CONNCACHE_LOCK(x) if((x)->share) \ + Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE) +#define CONNCACHE_UNLOCK(x) if((x)->share) \ + Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT) +#endif + struct connectbundle { int multiuse; /* supports multi-use */ size_t num_connections; /* Number of connections in the bundle */ @@ -54,13 +75,13 @@ void Curl_conncache_destroy(struct conncache *connc); /* return the correct bundle, to a host or a proxy */ struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn, - struct conncache *connc); -void Curl_conncache_unlock(struct Curl_easy *data); + struct conncache *connc, + const char **hostp); /* returns number of connections currently held in the connection cache */ size_t Curl_conncache_size(struct Curl_easy *data); -size_t Curl_conncache_bundle_size(struct connectdata *conn); -bool Curl_conncache_return_conn(struct connectdata *conn); +bool Curl_conncache_return_conn(struct Curl_easy *data, + struct connectdata *conn); CURLcode Curl_conncache_add_conn(struct conncache *connc, struct connectdata *conn) WARN_UNUSED_RESULT; void Curl_conncache_remove_conn(struct Curl_easy *data, diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index 002535b429a..29293f08745 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -75,6 +75,8 @@ #include "conncache.h" #include "multihandle.h" #include "system_win32.h" +#include "quic.h" +#include "socks.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -164,12 +166,13 @@ tcpkeepalive(struct Curl_easy *data, static CURLcode singleipconnect(struct connectdata *conn, - const Curl_addrinfo *ai, /* start connecting to this */ - curl_socket_t *sock); + const struct Curl_addrinfo *ai, /* start connecting to this */ + int tempindex); /* 0 or 1 among the temp ones */ /* * Curl_timeleft() returns the amount of milliseconds left allowed for the - * transfer/connection. If the value is negative, the timeout time has already + * transfer/connection. If the value is 0, there's no timeout (ie there's + * infinite time left). If the value is negative, the timeout time has already * elapsed. * * The start time is stored in progress.t_startsingle - as set with @@ -368,6 +371,11 @@ static CURLcode bindlocal(struct connectdata *conn, infof(data, "Name '%s' family %i resolved to '%s' family %i\n", dev, af, myhost, h->addr->ai_family); Curl_resolv_unlock(data, h); + if(af != h->addr->ai_family) { + /* bad IP version combo, signal the caller to try another address + family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + } done = 1; } else { @@ -548,13 +556,27 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) return rc; } -/* Used within the multi interface. Try next IP address, return TRUE if no +/* update tempaddr[tempindex] (to the next entry), makes sure to stick + to the correct family */ +static struct Curl_addrinfo *ainext(struct connectdata *conn, + int tempindex, + bool next) /* use next entry? */ +{ + struct Curl_addrinfo *ai = conn->tempaddr[tempindex]; + if(ai && next) + ai = ai->ai_next; + while(ai && (ai->ai_family != conn->tempfamily[tempindex])) + ai = ai->ai_next; + conn->tempaddr[tempindex] = ai; + return ai; +} + +/* Used within the multi interface. Try next IP address, returns error if no more address exists or error */ static CURLcode trynextip(struct connectdata *conn, int sockindex, int tempindex) { - const int other = tempindex ^ 1; CURLcode result = CURLE_COULDNT_CONNECT; /* First clean up after the failed socket. @@ -565,38 +587,15 @@ static CURLcode trynextip(struct connectdata *conn, conn->tempsock[tempindex] = CURL_SOCKET_BAD; if(sockindex == FIRSTSOCKET) { - Curl_addrinfo *ai = NULL; - int family = AF_UNSPEC; - - if(conn->tempaddr[tempindex]) { - /* find next address in the same protocol family */ - family = conn->tempaddr[tempindex]->ai_family; - ai = conn->tempaddr[tempindex]->ai_next; - } -#ifdef ENABLE_IPV6 - else if(conn->tempaddr[0]) { - /* happy eyeballs - try the other protocol family */ - int firstfamily = conn->tempaddr[0]->ai_family; - family = (firstfamily == AF_INET) ? AF_INET6 : AF_INET; - ai = conn->tempaddr[0]->ai_next; - } -#endif + struct Curl_addrinfo *ai = conn->tempaddr[tempindex]; while(ai) { - if(conn->tempaddr[other]) { - /* we can safely skip addresses of the other protocol family */ - while(ai && ai->ai_family != family) - ai = ai->ai_next; - } - if(ai) { - result = singleipconnect(conn, ai, &conn->tempsock[tempindex]); + result = singleipconnect(conn, ai, tempindex); if(result == CURLE_COULDNT_CONNECT) { - ai = ai->ai_next; + ai = ainext(conn, tempindex, TRUE); continue; } - - conn->tempaddr[tempindex] = ai; } break; } @@ -620,13 +619,10 @@ void Curl_persistconninfo(struct connectdata *conn) conn->data->info.conn_local_port = conn->local_port; } -UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, - long *port); - /* retrieves ip address and port from a sockaddr structure. note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */ -UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, - long *port) +bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, long *port) { struct sockaddr_in *si = NULL; #ifdef ENABLE_IPV6 @@ -634,6 +630,8 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, #endif #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX) struct sockaddr_un *su = NULL; +#else + (void)salen; #endif switch(sa->sa_family) { @@ -659,8 +657,12 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, #endif #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX) case AF_UNIX: - su = (struct sockaddr_un*)sa; - msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); + if(salen > (curl_socklen_t)sizeof(sa_family_t)) { + su = (struct sockaddr_un*)sa; + msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); + } + else + addr[0] = 0; /* socket with no name */ *port = 0; return TRUE; #endif @@ -678,62 +680,136 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, connection */ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) { - if(conn->socktype == SOCK_DGRAM) - /* there's no connection! */ - return; - + if(conn->transport == TRNSPRT_TCP) { #if defined(HAVE_GETPEERNAME) || defined(HAVE_GETSOCKNAME) - if(!conn->bits.reuse && !conn->bits.tcp_fastopen) { - struct Curl_easy *data = conn->data; - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssrem; - struct Curl_sockaddr_storage ssloc; - curl_socklen_t len; + if(!conn->bits.reuse && !conn->bits.tcp_fastopen) { + struct Curl_easy *data = conn->data; + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssrem; + struct Curl_sockaddr_storage ssloc; + curl_socklen_t plen; + curl_socklen_t slen; #ifdef HAVE_GETPEERNAME - len = sizeof(struct Curl_sockaddr_storage); - if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) { - int error = SOCKERRNO; - failf(data, "getpeername() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; - } + plen = sizeof(struct Curl_sockaddr_storage); + if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) { + int error = SOCKERRNO; + failf(data, "getpeername() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return; + } #endif #ifdef HAVE_GETSOCKNAME - len = sizeof(struct Curl_sockaddr_storage); - memset(&ssloc, 0, sizeof(ssloc)); - if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) { - int error = SOCKERRNO; - failf(data, "getsockname() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; - } + slen = sizeof(struct Curl_sockaddr_storage); + memset(&ssloc, 0, sizeof(ssloc)); + if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) { + int error = SOCKERRNO; + failf(data, "getsockname() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return; + } #endif #ifdef HAVE_GETPEERNAME - if(!getaddressinfo((struct sockaddr*)&ssrem, - conn->primary_ip, &conn->primary_port)) { - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; - } - memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN); + if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, + conn->primary_ip, &conn->primary_port)) { + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return; + } + memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN); #endif #ifdef HAVE_GETSOCKNAME - if(!getaddressinfo((struct sockaddr*)&ssloc, - conn->local_ip, &conn->local_port)) { - failf(data, "ssloc inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; - } + if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, + conn->local_ip, &conn->local_port)) { + failf(data, "ssloc inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return; + } #endif - } + } #else /* !HAVE_GETSOCKNAME && !HAVE_GETPEERNAME */ - (void)sockfd; /* unused */ + (void)sockfd; /* unused */ #endif + } /* end of TCP-only section */ /* persist connection info in session handle */ Curl_persistconninfo(conn); } +/* After a TCP connection to the proxy has been verified, this function does + the next magic steps. If 'done' isn't set TRUE, it is not done yet and + must be called again. + + Note: this function's sub-functions call failf() + +*/ +static CURLcode connect_SOCKS(struct connectdata *conn, int sockindex, + bool *done) +{ + CURLcode result = CURLE_OK; + +#ifndef CURL_DISABLE_PROXY + if(conn->bits.socksproxy) { + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ + const char * const host = + 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; + const int 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; + switch(conn->socks_proxy.proxytype) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd, + host, port, sockindex, conn, done); + break; + + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, + conn, done); + break; + + default: + failf(conn->data, "unknown proxytype option given"); + result = CURLE_COULDNT_CONNECT; + } /* switch proxytype */ + } + else +#else + (void)conn; + (void)sockindex; +#endif /* CURL_DISABLE_PROXY */ + *done = TRUE; /* no SOCKS proxy, so consider us connected */ + + return result; +} + +/* + * post_SOCKS() is called after a successful connect to the peer, which + * *could* be a SOCKS proxy + */ +static void post_SOCKS(struct connectdata *conn, + int sockindex, + bool *connected) +{ + conn->bits.tcpconnect[sockindex] = TRUE; + + *connected = TRUE; + if(sockindex == FIRSTSOCKET) + Curl_pgrsTime(conn->data, TIMER_CONNECT); /* connect done */ + Curl_updateconninfo(conn, conn->sock[sockindex]); + Curl_verboseconnect(conn); + conn->data->info.numconnects++; /* to track the number of connections made */ +} + /* * Curl_is_connected() checks if the socket has connected. */ @@ -747,8 +823,8 @@ CURLcode Curl_is_connected(struct connectdata *conn, timediff_t allow; int error = 0; struct curltime now; - int rc; - int i; + int rc = 0; + unsigned int i; DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); @@ -771,33 +847,62 @@ CURLcode Curl_is_connected(struct connectdata *conn, return CURLE_OPERATION_TIMEDOUT; } + if(SOCKS_STATE(conn->cnnct.state)) { + /* still doing SOCKS */ + result = connect_SOCKS(conn, sockindex, connected); + if(!result && *connected) + post_SOCKS(conn, sockindex, connected); + return result; + } + for(i = 0; i<2; i++) { const int other = i ^ 1; if(conn->tempsock[i] == CURL_SOCKET_BAD) continue; - + error = 0; +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) { + result = Curl_quic_is_connected(conn, i, connected); + if(!result && *connected) { + /* use this socket from now on */ + conn->sock[sockindex] = conn->tempsock[i]; + conn->ip_addr = conn->tempaddr[i]; + conn->tempsock[i] = CURL_SOCKET_BAD; + post_SOCKS(conn, sockindex, connected); + connkeep(conn, "HTTP/3 default"); + return CURLE_OK; + } + if(result) + error = SOCKERRNO; + } + else +#endif + { #ifdef mpeix - /* Call this function once now, and ignore the results. We do this to - "clear" the error state on the socket so that we can later read it - reliably. This is reported necessary on the MPE/iX operating system. */ - (void)verifyconnect(conn->tempsock[i], NULL); + /* Call this function once now, and ignore the results. We do this to + "clear" the error state on the socket so that we can later read it + reliably. This is reported necessary on the MPE/iX operating + system. */ + (void)verifyconnect(conn->tempsock[i], NULL); #endif - /* check socket for connect */ - rc = SOCKET_WRITABLE(conn->tempsock[i], 0); + /* check socket for connect */ + rc = SOCKET_WRITABLE(conn->tempsock[i], 0); + } if(rc == 0) { /* no connection yet */ - error = 0; - if(Curl_timediff(now, conn->connecttime) >= conn->timeoutms_per_addr) { - infof(data, "After %ldms connect time, move on!\n", - conn->timeoutms_per_addr); + if(Curl_timediff(now, conn->connecttime) >= + conn->timeoutms_per_addr[i]) { + infof(data, "After %" CURL_FORMAT_TIMEDIFF_T + "ms connect time, move on!\n", conn->timeoutms_per_addr[i]); error = ETIMEDOUT; } /* should we try another protocol family? */ - if(i == 0 && conn->tempaddr[1] == NULL && + if(i == 0 && !conn->bits.parallel_connect && (Curl_timediff(now, conn->connecttime) >= data->set.happy_eyeballs_timeout)) { + conn->bits.parallel_connect = TRUE; /* starting now */ trynextip(conn, sockindex, 1); } } @@ -819,18 +924,13 @@ CURLcode Curl_is_connected(struct connectdata *conn, conn->tempsock[other] = CURL_SOCKET_BAD; } - /* see if we need to do any proxy magic first once we connected */ - result = Curl_connected_proxy(conn, sockindex); - if(result) + /* see if we need to kick off any SOCKS proxy magic once we + connected */ + result = connect_SOCKS(conn, sockindex, connected); + if(result || !*connected) return result; - conn->bits.tcpconnect[sockindex] = TRUE; - - *connected = TRUE; - if(sockindex == FIRSTSOCKET) - Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ - Curl_updateconninfo(conn, conn->sock[sockindex]); - Curl_verboseconnect(conn); + post_SOCKS(conn, sockindex, connected); return CURLE_OK; } @@ -848,51 +948,70 @@ CURLcode Curl_is_connected(struct connectdata *conn, SET_SOCKERRNO(error); if(conn->tempaddr[i]) { CURLcode status; +#ifndef CURL_DISABLE_VERBOSE_STRINGS char ipaddress[MAX_IPADR_LEN]; char buffer[STRERROR_LEN]; - Curl_printable_address(conn->tempaddr[i], ipaddress, MAX_IPADR_LEN); + Curl_printable_address(conn->tempaddr[i], ipaddress, + sizeof(ipaddress)); infof(data, "connect to %s port %ld failed: %s\n", ipaddress, conn->port, Curl_strerror(error, buffer, sizeof(buffer))); +#endif - conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ? - allow : allow / 2; - + conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ? + allow : allow / 2; + ainext(conn, i, TRUE); status = trynextip(conn, sockindex, i); - if(status != CURLE_COULDNT_CONNECT - || conn->tempsock[other] == CURL_SOCKET_BAD) + if((status != CURLE_COULDNT_CONNECT) || + conn->tempsock[other] == CURL_SOCKET_BAD) /* the last attempt failed and no other sockets remain open */ result = status; } } } - if(result) { + if(result && + (conn->tempsock[0] == CURL_SOCKET_BAD) && + (conn->tempsock[1] == CURL_SOCKET_BAD)) { /* no more addresses to try */ const char *hostname; char buffer[STRERROR_LEN]; - /* if the first address family runs out of addresses to try before - the happy eyeball timeout, go ahead and try the next family now */ - if(conn->tempaddr[1] == NULL) { - result = trynextip(conn, sockindex, 1); - if(!result) - return result; - } + /* if the first address family runs out of addresses to try before the + happy eyeball timeout, go ahead and try the next family now */ + result = trynextip(conn, sockindex, 1); + if(!result) + return result; +#ifndef CURL_DISABLE_PROXY if(conn->bits.socksproxy) hostname = conn->socks_proxy.host.name; else if(conn->bits.httpproxy) hostname = conn->http_proxy.host.name; - else if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; + else +#endif + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; else hostname = conn->host.name; failf(data, "Failed to connect to %s port %ld: %s", hostname, conn->port, Curl_strerror(error, buffer, sizeof(buffer))); + + Curl_quic_disconnect(conn, 0); + Curl_quic_disconnect(conn, 1); + +#ifdef WSAETIMEDOUT + if(WSAETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#elif defined(ETIMEDOUT) + if(ETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#endif } + else + result = CURLE_OK; /* still trying */ return result; } @@ -900,14 +1019,12 @@ CURLcode Curl_is_connected(struct connectdata *conn, static void tcpnodelay(struct connectdata *conn, curl_socket_t sockfd) { #if defined(TCP_NODELAY) -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - struct Curl_easy *data = conn->data; -#endif curl_socklen_t onoff = (curl_socklen_t) 1; int level = IPPROTO_TCP; +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + struct Curl_easy *data = conn->data; char buffer[STRERROR_LEN]; - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) +#else (void) conn; #endif @@ -915,8 +1032,6 @@ static void tcpnodelay(struct connectdata *conn, curl_socket_t sockfd) sizeof(onoff)) < 0) infof(data, "Could not set TCP_NODELAY: %s\n", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - else - infof(data, "TCP_NODELAY set\n"); #else (void)conn; (void)sockfd; @@ -998,8 +1113,8 @@ void Curl_sndbufset(curl_socket_t sockfd) * having connected. */ static CURLcode singleipconnect(struct connectdata *conn, - const Curl_addrinfo *ai, - curl_socket_t *sockp) + const struct Curl_addrinfo *ai, + int tempindex) { struct Curl_sockaddr_ex addr; int rc = -1; @@ -1015,19 +1130,16 @@ static CURLcode singleipconnect(struct connectdata *conn, int optval = 1; #endif char buffer[STRERROR_LEN]; - + curl_socket_t *sockp = &conn->tempsock[tempindex]; *sockp = CURL_SOCKET_BAD; result = Curl_socket(conn, ai, &addr, &sockfd); if(result) - /* Failed to create the socket, but still return OK since we signal the - lack of socket as well. This allows the parent function to keep looping - over alternative addresses/socket families etc. */ - return CURLE_OK; + return result; /* store remote address and port used in this connection attempt */ - if(!getaddressinfo((struct sockaddr*)&addr.sa_addr, - ipaddress, &port)) { + if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen, + ipaddress, &port)) { /* malformed address or bug in inet_ntop, try next address */ failf(data, "sa_addr inet_ntop() failed with errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); @@ -1091,11 +1203,13 @@ static CURLcode singleipconnect(struct connectdata *conn, (void)curlx_nonblock(sockfd, TRUE); conn->connecttime = Curl_now(); - if(conn->num_addr > 1) - Curl_expire(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME); + if(conn->num_addr > 1) { + Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME); + Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2); + } - /* Connect TCP sockets, bind UDP */ - if(!isconnected && (conn->socktype == SOCK_STREAM)) { + /* Connect TCP and QUIC sockets */ + if(!isconnected && (conn->transport != TRNSPRT_UDP)) { if(conn->bits.tcp_fastopen) { #if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ # if defined(HAVE_BUILTIN_AVAILABLE) @@ -1124,8 +1238,6 @@ static CURLcode singleipconnect(struct connectdata *conn, if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, (void *)&optval, sizeof(optval)) < 0) infof(data, "Failed to enable TCP Fast Open on fd %d\n", sockfd); - else - infof(data, "TCP_FASTOPEN_CONNECT set\n"); rc = connect(sockfd, &addr.sa_addr, addr.addrlen); #elif defined(MSG_FASTOPEN) /* old Linux */ @@ -1141,6 +1253,16 @@ static CURLcode singleipconnect(struct connectdata *conn, if(-1 == rc) error = SOCKERRNO; +#ifdef ENABLE_QUIC + else if(conn->transport == TRNSPRT_QUIC) { + /* pass in 'sockfd' separately since it hasn't been put into the + tempsock array at this point */ + result = Curl_quic_connect(conn, sockfd, tempindex, + &addr.sa_addr, addr.addrlen); + if(result) + error = SOCKERRNO; + } +#endif } else { *sockp = sockfd; @@ -1193,7 +1315,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ struct Curl_easy *data = conn->data; struct curltime before = Curl_now(); CURLcode result = CURLE_COULDNT_CONNECT; - + int i; timediff_t timeout_ms = Curl_timeleft(data, &before, TRUE); if(timeout_ms < 0) { @@ -1203,30 +1325,37 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ } conn->num_addr = Curl_num_addresses(remotehost->addr); - conn->tempaddr[0] = remotehost->addr; - conn->tempaddr[1] = NULL; - conn->tempsock[0] = CURL_SOCKET_BAD; - conn->tempsock[1] = CURL_SOCKET_BAD; + conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr; + conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD; /* Max time for the next connection attempt */ - conn->timeoutms_per_addr = + conn->timeoutms_per_addr[0] = conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2; - - /* start connecting to first IP */ - while(conn->tempaddr[0]) { - result = singleipconnect(conn, conn->tempaddr[0], &(conn->tempsock[0])); - if(!result) - break; - conn->tempaddr[0] = conn->tempaddr[0]->ai_next; + conn->timeoutms_per_addr[1] = + conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2; + + conn->tempfamily[0] = conn->tempaddr[0]? + conn->tempaddr[0]->ai_family:0; + conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ? + AF_INET : AF_INET6; + ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */ + + DEBUGF(infof(data, "family0 == %s, family1 == %s\n", + conn->tempfamily[0] == AF_INET ? "v4" : "v6", + conn->tempfamily[1] == AF_INET ? "v4" : "v6")); + + /* get through the list in family order in case of quick failures */ + for(i = 0; (i < 2) && result; i++) { + while(conn->tempaddr[i]) { + result = singleipconnect(conn, conn->tempaddr[i], i); + if(!result) + break; + ainext(conn, i, TRUE); + } } - - if(conn->tempsock[0] == CURL_SOCKET_BAD) { - if(!result) - result = CURLE_COULDNT_CONNECT; + if(result) return result; - } - data->info.numconnects++; /* to track the number of connections made */ Curl_expire(conn->data, data->set.happy_eyeballs_timeout, EXPIRE_HAPPY_EYEBALLS); @@ -1326,12 +1455,11 @@ int Curl_closesocket(struct connectdata *conn, curl_socket_t sock) { if(conn && conn->fclosesocket) { - if((sock == conn->sock[SECONDARYSOCKET]) && - conn->sock_accepted[SECONDARYSOCKET]) + if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted) /* if this socket matches the second socket, and that was created with accept, then we MUST NOT call the callback but clear the accepted status */ - conn->sock_accepted[SECONDARYSOCKET] = FALSE; + conn->bits.sock_accepted = FALSE; else { int rc; Curl_multi_closed(conn->data, sock); @@ -1361,7 +1489,7 @@ int Curl_closesocket(struct connectdata *conn, * */ CURLcode Curl_socket(struct connectdata *conn, - const Curl_addrinfo *ai, + const struct Curl_addrinfo *ai, struct Curl_sockaddr_ex *addr, curl_socket_t *sockfd) { @@ -1381,8 +1509,9 @@ CURLcode Curl_socket(struct connectdata *conn, */ addr->family = ai->ai_family; - addr->socktype = conn->socktype; - addr->protocol = conn->socktype == SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol; + addr->socktype = (conn->transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; + addr->protocol = conn->transport != TRNSPRT_TCP ? IPPROTO_UDP : + ai->ai_protocol; addr->addrlen = ai->ai_addrlen; if(addr->addrlen > sizeof(struct Curl_sockaddr_storage)) @@ -1413,6 +1542,11 @@ CURLcode Curl_socket(struct connectdata *conn, /* no socket, no connection */ return CURLE_COULDNT_CONNECT; + if(conn->transport == TRNSPRT_QUIC) { + /* QUIC sockets need to be nonblocking */ + (void)curlx_nonblock(*sockfd, TRUE); + } + #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if(conn->scope_id && (addr->family == AF_INET6)) { struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; @@ -1437,6 +1571,7 @@ void Curl_conncontrol(struct connectdata *conn, /* close if a connection, or a stream that isn't multiplexed */ bool closeit = (ctrl == CONNCTRL_CONNECTION) || ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM)); + DEBUGASSERT(conn); if((ctrl == CONNCTRL_STREAM) && (conn->handler->flags & PROTOPT_STREAM)) DEBUGF(infof(conn->data, "Kill stream: %s\n", reason)); @@ -1452,6 +1587,7 @@ void Curl_conncontrol(struct connectdata *conn, bool Curl_conn_data_pending(struct connectdata *conn, int sockindex) { int readable; + DEBUGASSERT(conn); if(Curl_ssl_data_pending(conn, sockindex) || Curl_recv_has_postponed_data(conn, sockindex)) diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h index 6a5c755cc1c..6fd9ea87890 100644 --- a/Utilities/cmcurl/lib/connect.h +++ b/Utilities/cmcurl/lib/connect.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -51,6 +51,9 @@ timediff_t Curl_timeleft(struct Curl_easy *data, curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, struct connectdata **connp); +bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, long *port); + /* * Check if a connection seems to be alive. */ @@ -102,7 +105,7 @@ struct Curl_sockaddr_ex { * */ CURLcode Curl_socket(struct connectdata *conn, - const Curl_addrinfo *ai, + const struct Curl_addrinfo *ai, struct Curl_sockaddr_ex *addr, curl_socket_t *sockfd); diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index 6d47537883d..e2e68a11669 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -81,11 +81,11 @@ typedef enum { } zlibInitState; /* Writer parameters. */ -typedef struct { +struct zlib_params { zlibInitState zlib_init; /* zlib init state */ uInt trailerlen; /* Remaining trailer byte count. */ z_stream z; /* State structure for zlib. */ -} zlib_params; +}; static voidpf @@ -133,7 +133,8 @@ exit_zlib(struct connectdata *conn, return result; } -static CURLcode process_trailer(struct connectdata *conn, zlib_params *zp) +static CURLcode process_trailer(struct connectdata *conn, + struct zlib_params *zp) { z_stream *z = &zp->z; CURLcode result = CURLE_OK; @@ -157,9 +158,10 @@ static CURLcode process_trailer(struct connectdata *conn, zlib_params *zp) } static CURLcode inflate_stream(struct connectdata *conn, - contenc_writer *writer, zlibInitState started) + struct contenc_writer *writer, + zlibInitState started) { - zlib_params *zp = (zlib_params *) &writer->params; + struct zlib_params *zp = (struct zlib_params *) &writer->params; z_stream *z = &zp->z; /* zlib state structure */ uInt nread = z->avail_in; Bytef *orig_in = z->next_in; @@ -259,9 +261,9 @@ static CURLcode inflate_stream(struct connectdata *conn, /* Deflate handler. */ static CURLcode deflate_init_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { - zlib_params *zp = (zlib_params *) &writer->params; + struct zlib_params *zp = (struct zlib_params *) &writer->params; z_stream *z = &zp->z; /* zlib state structure */ if(!writer->downstream) @@ -278,10 +280,10 @@ static CURLcode deflate_init_writer(struct connectdata *conn, } static CURLcode deflate_unencode_write(struct connectdata *conn, - contenc_writer *writer, + struct contenc_writer *writer, const char *buf, size_t nbytes) { - zlib_params *zp = (zlib_params *) &writer->params; + struct zlib_params *zp = (struct zlib_params *) &writer->params; z_stream *z = &zp->z; /* zlib state structure */ /* Set the compressed input when this function is called */ @@ -296,29 +298,29 @@ static CURLcode deflate_unencode_write(struct connectdata *conn, } static void deflate_close_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { - zlib_params *zp = (zlib_params *) &writer->params; + struct zlib_params *zp = (struct zlib_params *) &writer->params; z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(conn, z, &zp->zlib_init, CURLE_OK); } -static const content_encoding deflate_encoding = { +static const struct content_encoding deflate_encoding = { "deflate", NULL, deflate_init_writer, deflate_unencode_write, deflate_close_writer, - sizeof(zlib_params) + sizeof(struct zlib_params) }; /* Gzip handler. */ static CURLcode gzip_init_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { - zlib_params *zp = (zlib_params *) &writer->params; + struct zlib_params *zp = (struct zlib_params *) &writer->params; z_stream *z = &zp->z; /* zlib state structure */ if(!writer->downstream) @@ -432,10 +434,10 @@ static enum { #endif static CURLcode gzip_unencode_write(struct connectdata *conn, - contenc_writer *writer, + struct contenc_writer *writer, const char *buf, size_t nbytes) { - zlib_params *zp = (zlib_params *) &writer->params; + struct zlib_params *zp = (struct zlib_params *) &writer->params; z_stream *z = &zp->z; /* zlib state structure */ if(zp->zlib_init == ZLIB_INIT_GZIP) { @@ -560,33 +562,31 @@ static CURLcode gzip_unencode_write(struct connectdata *conn, } static void gzip_close_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { - zlib_params *zp = (zlib_params *) &writer->params; + struct zlib_params *zp = (struct zlib_params *) &writer->params; z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(conn, z, &zp->zlib_init, CURLE_OK); } -static const content_encoding gzip_encoding = { +static const struct content_encoding gzip_encoding = { "gzip", "x-gzip", gzip_init_writer, gzip_unencode_write, gzip_close_writer, - sizeof(zlib_params) + sizeof(struct zlib_params) }; #endif /* HAVE_LIBZ */ #ifdef HAVE_BROTLI - /* Writer parameters. */ -typedef struct { +struct brotli_params { BrotliDecoderState *br; /* State structure for brotli. */ -} brotli_params; - +}; static CURLcode brotli_map_error(BrotliDecoderErrorCode be) { @@ -627,10 +627,9 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be) } static CURLcode brotli_init_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { - brotli_params *bp = (brotli_params *) &writer->params; - + struct brotli_params *bp = (struct brotli_params *) &writer->params; (void) conn; if(!writer->downstream) @@ -641,10 +640,10 @@ static CURLcode brotli_init_writer(struct connectdata *conn, } static CURLcode brotli_unencode_write(struct connectdata *conn, - contenc_writer *writer, + struct contenc_writer *writer, const char *buf, size_t nbytes) { - brotli_params *bp = (brotli_params *) &writer->params; + struct brotli_params *bp = (struct brotli_params *) &writer->params; const uint8_t *src = (const uint8_t *) buf; char *decomp; uint8_t *dst; @@ -689,10 +688,9 @@ static CURLcode brotli_unencode_write(struct connectdata *conn, } static void brotli_close_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { - brotli_params *bp = (brotli_params *) &writer->params; - + struct brotli_params *bp = (struct brotli_params *) &writer->params; (void) conn; if(bp->br) { @@ -701,40 +699,40 @@ static void brotli_close_writer(struct connectdata *conn, } } -static const content_encoding brotli_encoding = { +static const struct content_encoding brotli_encoding = { "br", NULL, brotli_init_writer, brotli_unencode_write, brotli_close_writer, - sizeof(brotli_params) + sizeof(struct brotli_params) }; #endif /* Identity handler. */ static CURLcode identity_init_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { (void) conn; return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; } static CURLcode identity_unencode_write(struct connectdata *conn, - contenc_writer *writer, + struct contenc_writer *writer, const char *buf, size_t nbytes) { return Curl_unencode_write(conn, writer->downstream, buf, nbytes); } static void identity_close_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { (void) conn; (void) writer; } -static const content_encoding identity_encoding = { +static const struct content_encoding identity_encoding = { "identity", "none", identity_init_writer, @@ -745,7 +743,7 @@ static const content_encoding identity_encoding = { /* supported content encodings table. */ -static const content_encoding * const encodings[] = { +static const struct content_encoding * const encodings[] = { &identity_encoding, #ifdef HAVE_LIBZ &deflate_encoding, @@ -762,8 +760,8 @@ static const content_encoding * const encodings[] = { char *Curl_all_content_encodings(void) { size_t len = 0; - const content_encoding * const *cep; - const content_encoding *ce; + const struct content_encoding * const *cep; + const struct content_encoding *ce; char *ace; for(cep = encodings; *cep; cep++) { @@ -796,14 +794,14 @@ char *Curl_all_content_encodings(void) /* Real client writer: no downstream. */ static CURLcode client_init_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { (void) conn; return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK; } static CURLcode client_unencode_write(struct connectdata *conn, - contenc_writer *writer, + struct contenc_writer *writer, const char *buf, size_t nbytes) { struct Curl_easy *data = conn->data; @@ -818,13 +816,13 @@ static CURLcode client_unencode_write(struct connectdata *conn, } static void client_close_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { (void) conn; (void) writer; } -static const content_encoding client_encoding = { +static const struct content_encoding client_encoding = { NULL, NULL, client_init_writer, @@ -836,14 +834,14 @@ static const content_encoding client_encoding = { /* Deferred error dummy writer. */ static CURLcode error_init_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { (void) conn; return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; } static CURLcode error_unencode_write(struct connectdata *conn, - contenc_writer *writer, + struct contenc_writer *writer, const char *buf, size_t nbytes) { char *all = Curl_all_content_encodings(); @@ -861,13 +859,13 @@ static CURLcode error_unencode_write(struct connectdata *conn, } static void error_close_writer(struct connectdata *conn, - contenc_writer *writer) + struct contenc_writer *writer) { (void) conn; (void) writer; } -static const content_encoding error_encoding = { +static const struct content_encoding error_encoding = { NULL, NULL, error_init_writer, @@ -877,12 +875,13 @@ static const content_encoding error_encoding = { }; /* Create an unencoding writer stage using the given handler. */ -static contenc_writer *new_unencoding_writer(struct connectdata *conn, - const content_encoding *handler, - contenc_writer *downstream) +static struct contenc_writer * +new_unencoding_writer(struct connectdata *conn, + const struct content_encoding *handler, + struct contenc_writer *downstream) { - size_t sz = offsetof(contenc_writer, params) + handler->paramsize; - contenc_writer *writer = (contenc_writer *) calloc(1, sz); + size_t sz = offsetof(struct contenc_writer, params) + handler->paramsize; + struct contenc_writer *writer = (struct contenc_writer *)calloc(1, sz); if(writer) { writer->handler = handler; @@ -897,7 +896,8 @@ static contenc_writer *new_unencoding_writer(struct connectdata *conn, } /* Write data using an unencoding writer stack. */ -CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer, +CURLcode Curl_unencode_write(struct connectdata *conn, + struct contenc_writer *writer, const char *buf, size_t nbytes) { if(!nbytes) @@ -910,7 +910,7 @@ void Curl_unencode_cleanup(struct connectdata *conn) { struct Curl_easy *data = conn->data; struct SingleRequest *k = &data->req; - contenc_writer *writer = k->writer_stack; + struct contenc_writer *writer = k->writer_stack; while(writer) { k->writer_stack = writer->downstream; @@ -921,12 +921,13 @@ void Curl_unencode_cleanup(struct connectdata *conn) } /* Find the content encoding by name. */ -static const content_encoding *find_encoding(const char *name, size_t len) +static const struct content_encoding *find_encoding(const char *name, + size_t len) { - const content_encoding * const *cep; + const struct content_encoding * const *cep; for(cep = encodings; *cep; cep++) { - const content_encoding *ce = *cep; + const struct content_encoding *ce = *cep; if((strncasecompare(name, ce->name, len) && !ce->name[len]) || (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) return ce; @@ -962,8 +963,8 @@ CURLcode Curl_build_unencoding_stack(struct connectdata *conn, Curl_httpchunk_init(conn); /* init our chunky engine. */ } else if(namelen) { - const content_encoding *encoding = find_encoding(name, namelen); - contenc_writer *writer; + const struct content_encoding *encoding = find_encoding(name, namelen); + struct contenc_writer *writer; if(!k->writer_stack) { k->writer_stack = new_unencoding_writer(conn, &client_encoding, NULL); @@ -997,7 +998,8 @@ CURLcode Curl_build_unencoding_stack(struct connectdata *conn, return CURLE_NOT_BUILT_IN; } -CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer, +CURLcode Curl_unencode_write(struct connectdata *conn, + struct contenc_writer *writer, const char *buf, size_t nbytes) { (void) conn; diff --git a/Utilities/cmcurl/lib/content_encoding.h b/Utilities/cmcurl/lib/content_encoding.h index 4cd52be6226..bdd3f1c8c6a 100644 --- a/Utilities/cmcurl/lib/content_encoding.h +++ b/Utilities/cmcurl/lib/content_encoding.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,31 +23,31 @@ ***************************************************************************/ #include "curl_setup.h" -/* Decoding writer. */ -typedef struct contenc_writer_s contenc_writer; -typedef struct content_encoding_s content_encoding; - -struct contenc_writer_s { - const content_encoding *handler; /* Encoding handler. */ - contenc_writer *downstream; /* Downstream writer. */ +struct contenc_writer { + const struct content_encoding *handler; /* Encoding handler. */ + struct contenc_writer *downstream; /* Downstream writer. */ void *params; /* Encoding-specific storage (variable length). */ }; /* Content encoding writer. */ -struct content_encoding_s { +struct content_encoding { const char *name; /* Encoding name. */ const char *alias; /* Encoding name alias. */ - CURLcode (*init_writer)(struct connectdata *conn, contenc_writer *writer); - CURLcode (*unencode_write)(struct connectdata *conn, contenc_writer *writer, + CURLcode (*init_writer)(struct connectdata *conn, + struct contenc_writer *writer); + CURLcode (*unencode_write)(struct connectdata *conn, + struct contenc_writer *writer, const char *buf, size_t nbytes); - void (*close_writer)(struct connectdata *conn, contenc_writer *writer); + void (*close_writer)(struct connectdata *conn, + struct contenc_writer *writer); size_t paramsize; }; CURLcode Curl_build_unencoding_stack(struct connectdata *conn, const char *enclist, int maybechunked); -CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer, +CURLcode Curl_unencode_write(struct connectdata *conn, + struct contenc_writer *writer, const char *buf, size_t nbytes); void Curl_unencode_cleanup(struct connectdata *conn); char *Curl_all_content_encodings(void); diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index 9a9e14d0129..cb7d94b1022 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -96,6 +96,9 @@ Example set of cookies: #include "curl_get_line.h" #include "curl_memrchr.h" #include "inet_pton.h" +#include "parsedate.h" +#include "rand.h" +#include "rename.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -242,18 +245,17 @@ static bool pathmatch(const char *cookie_path, const char *request_uri) */ static const char *get_top_domain(const char * const domain, size_t *outlen) { - size_t len; + size_t len = 0; const char *first = NULL, *last; - if(!domain) - return NULL; - - len = strlen(domain); - last = memrchr(domain, '.', len); - if(last) { - first = memrchr(domain, '.', (last - domain)); - if(first) - len -= (++first - domain); + if(domain) { + len = strlen(domain); + last = memrchr(domain, '.', len); + if(last) { + first = memrchr(domain, '.', (last - domain)); + if(first) + len -= (++first - domain); + } } if(outlen) @@ -536,9 +538,9 @@ Curl_cookie_add(struct Curl_easy *data, * only test for names where that can possibly be true. */ if(nlen > 3 && name[0] == '_' && name[1] == '_') { - if(strncasecompare("__Secure-", name, 9)) + if(!strncmp("__Secure-", name, 9)) co->prefix |= COOKIE_PREFIX__SECURE; - else if(strncasecompare("__Host-", name, 7)) + else if(!strncmp("__Host-", name, 7)) co->prefix |= COOKIE_PREFIX__HOST; } @@ -715,7 +717,7 @@ Curl_cookie_add(struct Curl_easy *data, else if(co->expirestr) { /* Note that if the date couldn't get parsed for whatever reason, the cookie will be treated as a session cookie */ - co->expires = curl_getdate(co->expirestr, NULL); + co->expires = Curl_getdate_capped(co->expirestr); /* Session cookies have expires set to 0 so if we get that back from the date parser let's add a second to make it a @@ -753,7 +755,7 @@ Curl_cookie_add(struct Curl_easy *data, co->path = malloc(pathlen + 1); /* one extra for the zero byte */ if(co->path) { memcpy(co->path, path, pathlen); - co->path[pathlen] = 0; /* zero terminate */ + co->path[pathlen] = 0; /* null-terminate */ co->spath = sanitize_cookie_path(co->path); if(!co->spath) badcookie = TRUE; /* out of memory bad */ @@ -819,22 +821,14 @@ Curl_cookie_add(struct Curl_easy *data, badcookie = TRUE; break; case 1: - /* This field got its explanation on the 23rd of May 2001 by - Andrés García: - - flag: A TRUE/FALSE value indicating if all machines within a given - domain can access the variable. This value is set automatically by - the browser, depending on the value you set for the domain. - - As far as I can see, it is set to true when the cookie says + /* flag: A TRUE/FALSE value indicating if all machines within a given + domain can access the variable. Set TRUE when the cookie says .domain.com and to false when the domain is complete www.domain.com */ co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE; break; case 2: - /* It turns out, that sometimes the file format allows the path - field to remain not filled in, we try to detect this and work - around it! Andrés García made us aware of this... */ + /* The file format allows the path field to remain not filled in */ if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { /* only if the path doesn't look like a boolean option! */ co->path = strdup(ptr); @@ -1053,7 +1047,7 @@ Curl_cookie_add(struct Curl_easy *data, *clist = *co; /* then store all the new data */ - free(co); /* free the newly alloced memory */ + free(co); /* free the newly allocated memory */ co = clist; /* point to the previous struct instead */ /* We have replaced a cookie, now skip the rest of the list but @@ -1098,6 +1092,8 @@ Curl_cookie_add(struct Curl_easy *data, * * If 'newsession' is TRUE, discard all "session cookies" on read from file. * + * Note that 'data' might be called as NULL pointer. + * * Returns NULL on out of memory. Invalid cookies are ignored. ****************************************************************************/ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, @@ -1168,6 +1164,8 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, } c->running = TRUE; /* now, we're running */ + if(data) + data->state.cookie_engine = TRUE; return c; @@ -1504,11 +1502,14 @@ static char *get_netscape_format(const struct Cookie *co) * * The function returns non-zero on write failure. */ -static int cookie_output(struct CookieInfo *c, const char *dumphere) +static int cookie_output(struct Curl_easy *data, + struct CookieInfo *c, const char *filename) { struct Cookie *co; - FILE *out; + FILE *out = NULL; bool use_stdout = FALSE; + char *tempstore = NULL; + bool error = false; if(!c) /* no cookie engine alive */ @@ -1517,16 +1518,24 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) /* at first, remove expired cookies */ remove_expired(c); - if(!strcmp("-", dumphere)) { + if(!strcmp("-", filename)) { /* use stdout */ out = stdout; use_stdout = TRUE; } else { - out = fopen(dumphere, FOPEN_WRITETEXT); - if(!out) { - return 1; /* failure */ - } + unsigned char randsuffix[9]; + + if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix))) + return 2; + + tempstore = aprintf("%s.%s.tmp", filename, randsuffix); + if(!tempstore) + return 1; + + out = fopen(tempstore, FOPEN_WRITETEXT); + if(!out) + goto error; } fputs("# Netscape HTTP Cookie File\n" @@ -1536,35 +1545,31 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) if(c->numcookies) { unsigned int i; - unsigned int j; + size_t nvalid = 0; struct Cookie **array; - array = malloc(sizeof(struct Cookie *) * c->numcookies); + array = calloc(1, sizeof(struct Cookie *) * c->numcookies); if(!array) { - if(!use_stdout) - fclose(out); - return 1; + goto error; } - j = 0; + /* only sort the cookies with a domain property */ for(i = 0; i < COOKIE_HASH_SIZE; i++) { for(co = c->cookies[i]; co; co = co->next) { if(!co->domain) continue; - array[j++] = co; + array[nvalid++] = co; } } - qsort(array, c->numcookies, sizeof(struct Cookie *), cookie_sort_ct); + qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct); - for(i = 0; i < j; i++) { + for(i = 0; i < nvalid; i++) { char *format_ptr = get_netscape_format(array[i]); if(format_ptr == NULL) { fprintf(out, "#\n# Fatal libcurl error\n"); free(array); - if(!use_stdout) - fclose(out); - return 1; + goto error; } fprintf(out, "%s\n", format_ptr); free(format_ptr); @@ -1572,10 +1577,24 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) free(array); } - if(!use_stdout) + + if(!use_stdout) { fclose(out); + out = NULL; + if(Curl_rename(tempstore, filename)) { + unlink(tempstore); + goto error; + } + } - return 0; + goto cleanup; +error: + error = true; +cleanup: + if(out && !use_stdout) + fclose(out); + free(tempstore); + return error ? 1 : 0; } static struct curl_slist *cookie_list(struct Curl_easy *data) @@ -1621,7 +1640,7 @@ struct curl_slist *Curl_cookie_list(struct Curl_easy *data) return list; } -void Curl_flush_cookies(struct Curl_easy *data, int cleanup) +void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) { if(data->set.str[STRING_COOKIEJAR]) { if(data->change.cookielist) { @@ -1634,7 +1653,7 @@ void Curl_flush_cookies(struct Curl_easy *data, int cleanup) Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); /* if we have a destination file for all the cookies to get dumped to */ - if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR])) + if(cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR])) infof(data, "WARNING: failed to save cookies in %s\n", data->set.str[STRING_COOKIEJAR]); } @@ -1650,6 +1669,7 @@ void Curl_flush_cookies(struct Curl_easy *data, int cleanup) if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { Curl_cookie_cleanup(data->cookies); + data->cookies = NULL; } Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h index b2730cfb91d..b3865e601a7 100644 --- a/Utilities/cmcurl/lib/cookie.h +++ b/Utilities/cmcurl/lib/cookie.h @@ -109,7 +109,7 @@ void Curl_cookie_clearsess(struct CookieInfo *cookies); #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, int cleanup); +void Curl_flush_cookies(struct Curl_easy *data, bool cleanup); void Curl_cookie_cleanup(struct CookieInfo *); struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, const char *, struct CookieInfo *, bool); diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c index 16c4779c1ec..947d0d37567 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.c +++ b/Utilities/cmcurl/lib/curl_addrinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -50,10 +50,6 @@ # define in_addr_t unsigned long #endif -#if defined(WIN32) && defined(USE_UNIX_SOCKETS) -#include -#endif - #include #include "curl_addrinfo.h" @@ -82,16 +78,13 @@ #endif void -Curl_freeaddrinfo(Curl_addrinfo *cahead) +Curl_freeaddrinfo(struct Curl_addrinfo *cahead) { - Curl_addrinfo *vqualifier canext; - Curl_addrinfo *ca; + struct Curl_addrinfo *vqualifier canext; + struct Curl_addrinfo *ca; - for(ca = cahead; ca != NULL; ca = canext) { - free(ca->ai_addr); - free(ca->ai_canonname); + for(ca = cahead; ca; ca = canext) { canext = ca->ai_next; - free(ca); } } @@ -116,13 +109,13 @@ int Curl_getaddrinfo_ex(const char *nodename, const char *servname, const struct addrinfo *hints, - Curl_addrinfo **result) + struct Curl_addrinfo **result) { const struct addrinfo *ai; struct addrinfo *aihead; - Curl_addrinfo *cafirst = NULL; - Curl_addrinfo *calast = NULL; - Curl_addrinfo *ca; + struct Curl_addrinfo *cafirst = NULL; + struct Curl_addrinfo *calast = NULL; + struct Curl_addrinfo *ca; size_t ss_size; int error; @@ -135,7 +128,7 @@ Curl_getaddrinfo_ex(const char *nodename, /* traverse the addrinfo list */ for(ai = aihead; ai != NULL; ai = ai->ai_next) { - + size_t namelen = ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0; /* ignore elements with unsupported address family, */ /* settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) @@ -155,7 +148,7 @@ Curl_getaddrinfo_ex(const char *nodename, if((size_t)ai->ai_addrlen < ss_size) continue; - ca = malloc(sizeof(Curl_addrinfo)); + ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen); if(!ca) { error = EAI_MEMORY; break; @@ -173,22 +166,12 @@ Curl_getaddrinfo_ex(const char *nodename, ca->ai_canonname = NULL; ca->ai_next = NULL; - ca->ai_addr = malloc(ss_size); - if(!ca->ai_addr) { - error = EAI_MEMORY; - free(ca); - break; - } + ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); memcpy(ca->ai_addr, ai->ai_addr, ss_size); - if(ai->ai_canonname != NULL) { - ca->ai_canonname = strdup(ai->ai_canonname); - if(!ca->ai_canonname) { - error = EAI_MEMORY; - free(ca->ai_addr); - free(ca); - break; - } + if(namelen) { + ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size); + memcpy(ca->ai_canonname, ai->ai_canonname, namelen); } /* if the return list is empty, this becomes the first element */ @@ -256,7 +239,6 @@ Curl_getaddrinfo_ex(const char *nodename, * struct sockaddr *ai_addr; * struct Curl_addrinfo *ai_next; * }; - * typedef struct Curl_addrinfo Curl_addrinfo; * * hostent defined in * @@ -273,12 +255,12 @@ Curl_getaddrinfo_ex(const char *nodename, * #define h_addr h_addr_list[0] */ -Curl_addrinfo * +struct Curl_addrinfo * Curl_he2ai(const struct hostent *he, int port) { - Curl_addrinfo *ai; - Curl_addrinfo *prevai = NULL; - Curl_addrinfo *firstai = NULL; + struct Curl_addrinfo *ai; + struct Curl_addrinfo *prevai = NULL; + struct Curl_addrinfo *firstai = NULL; struct sockaddr_in *addr; #ifdef ENABLE_IPV6 struct sockaddr_in6 *addr6; @@ -294,8 +276,8 @@ Curl_he2ai(const struct hostent *he, int port) DEBUGASSERT((he->h_name != NULL) && (he->h_addr_list != NULL)); for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) { - size_t ss_size; + size_t namelen = strlen(he->h_name) + 1; /* include zero termination */ #ifdef ENABLE_IPV6 if(he->h_addrtype == AF_INET6) ss_size = sizeof(struct sockaddr_in6); @@ -303,24 +285,17 @@ Curl_he2ai(const struct hostent *he, int port) #endif ss_size = sizeof(struct sockaddr_in); - ai = calloc(1, sizeof(Curl_addrinfo)); + /* allocate memory to told the struct, the address and the name */ + ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + namelen); if(!ai) { result = CURLE_OUT_OF_MEMORY; break; } - ai->ai_canonname = strdup(he->h_name); - if(!ai->ai_canonname) { - result = CURLE_OUT_OF_MEMORY; - free(ai); - break; - } - ai->ai_addr = calloc(1, ss_size); - if(!ai->ai_addr) { - result = CURLE_OUT_OF_MEMORY; - free(ai->ai_canonname); - free(ai); - break; - } + /* put the address after the struct */ + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); + /* then put the name after the address */ + ai->ai_canonname = (char *)ai->ai_addr + ss_size; + memcpy(ai->ai_canonname, he->h_name, namelen); if(!firstai) /* store the pointer we want to return from this function */ @@ -393,10 +368,10 @@ struct namebuff { * given address/host */ -Curl_addrinfo * +struct Curl_addrinfo * Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port) { - Curl_addrinfo *ai; + struct Curl_addrinfo *ai; #if defined(__VMS) && \ defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64) @@ -469,7 +444,7 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port) * Given an IPv4 or IPv6 dotted string address, this converts it to a proper * allocated Curl_addrinfo struct and returns it. */ -Curl_addrinfo *Curl_str2addr(char *address, int port) +struct Curl_addrinfo *Curl_str2addr(char *address, int port) { struct in_addr in; if(Curl_inet_pton(AF_INET, address, &in) > 0) @@ -492,22 +467,19 @@ Curl_addrinfo *Curl_str2addr(char *address, int port) * struct initialized with this path. * Set '*longpath' to TRUE if the error is a too long path. */ -Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, bool abstract) +struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, + bool abstract) { - Curl_addrinfo *ai; + struct Curl_addrinfo *ai; struct sockaddr_un *sa_un; size_t path_len; *longpath = FALSE; - ai = calloc(1, sizeof(Curl_addrinfo)); + ai = calloc(1, sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un)); if(!ai) return NULL; - ai->ai_addr = calloc(1, sizeof(struct sockaddr_un)); - if(!ai->ai_addr) { - free(ai); - return NULL; - } + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); sa_un = (void *) ai->ai_addr; sa_un->sun_family = AF_UNIX; @@ -515,7 +487,6 @@ Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, bool abstract) /* sun_path must be able to store the NUL-terminated path */ path_len = strlen(path) + 1; if(path_len > sizeof(sa_un->sun_path)) { - free(ai->ai_addr); free(ai); *longpath = TRUE; return NULL; @@ -598,9 +569,9 @@ curl_dbg_getaddrinfo(const char *hostname, * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and Mac OS X * 10.11.5. */ -void Curl_addrinfo_set_port(Curl_addrinfo *addrinfo, int port) +void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) { - Curl_addrinfo *ca; + struct Curl_addrinfo *ca; struct sockaddr_in *addr; #ifdef ENABLE_IPV6 struct sockaddr_in6 *addr6; diff --git a/Utilities/cmcurl/lib/curl_addrinfo.h b/Utilities/cmcurl/lib/curl_addrinfo.h index 205e121ea6b..a0cade614e1 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.h +++ b/Utilities/cmcurl/lib/curl_addrinfo.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,7 +40,6 @@ # include #endif - /* * Curl_addrinfo is our internal struct definition that we use to allow * consistent internal handling of this data. We use this even when the @@ -58,29 +57,29 @@ struct Curl_addrinfo { struct sockaddr *ai_addr; struct Curl_addrinfo *ai_next; }; -typedef struct Curl_addrinfo Curl_addrinfo; void -Curl_freeaddrinfo(Curl_addrinfo *cahead); +Curl_freeaddrinfo(struct Curl_addrinfo *cahead); #ifdef HAVE_GETADDRINFO int Curl_getaddrinfo_ex(const char *nodename, const char *servname, const struct addrinfo *hints, - Curl_addrinfo **result); + struct Curl_addrinfo **result); #endif -Curl_addrinfo * +struct Curl_addrinfo * Curl_he2ai(const struct hostent *he, int port); -Curl_addrinfo * +struct Curl_addrinfo * Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port); -Curl_addrinfo *Curl_str2addr(char *dotted, int port); +struct Curl_addrinfo *Curl_str2addr(char *dotted, int port); #ifdef USE_UNIX_SOCKETS -Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, bool abstract); +struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, + bool abstract); #endif #if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ @@ -98,7 +97,7 @@ curl_dbg_getaddrinfo(const char *hostname, const char *service, #ifdef HAVE_GETADDRINFO #ifdef USE_RESOLVE_ON_IPS -void Curl_addrinfo_set_port(Curl_addrinfo *addrinfo, int port); +void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port); #else #define Curl_addrinfo_set_port(x,y) #endif diff --git a/Utilities/cmcurl/lib/curl_base64.h b/Utilities/cmcurl/lib/curl_base64.h index 7e9fc26062a..cfb6ee75b27 100644 --- a/Utilities/cmcurl/lib/curl_base64.h +++ b/Utilities/cmcurl/lib/curl_base64.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index b285c3fdd5d..00ae65801d3 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake @@ -1,5 +1,28 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ /* lib/curl_config.h.in. Generated somehow by cmake. */ +#include + /* when building libcurl itself */ #cmakedefine BUILDING_LIBCURL 1 @@ -33,6 +56,9 @@ /* to disable LDAPS */ #cmakedefine CURL_DISABLE_LDAPS 1 +/* to enable MQTT */ +#undef CURL_ENABLE_MQTT + /* to disable POP3 */ #cmakedefine CURL_DISABLE_POP3 1 @@ -64,6 +90,9 @@ #define CURL_EXTERN_SYMBOL #endif +/* Allow SMB to work on Windows */ +#cmakedefine USE_WIN32_CRYPTO + /* Use Windows LDAP implementation */ #cmakedefine USE_WIN32_LDAP 1 @@ -133,15 +162,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_CRYPTO_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_DES_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DLFCN_H 1 -/* Define to 1 if you have the `ENGINE_load_builtin_engines' function. */ -#cmakedefine HAVE_ENGINE_LOAD_BUILTIN_ENGINES 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ERRNO_H 1 @@ -232,6 +255,9 @@ /* Define to 1 if you have the `getsockname' function. */ #cmakedefine HAVE_GETSOCKNAME 1 +/* Define to 1 if you have the `if_nametoindex' function. */ +#cmakedefine HAVE_IF_NAMETOINDEX 1 + /* Define to 1 if you have the `getpwuid' function. */ #cmakedefine HAVE_GETPWUID 1 @@ -395,8 +421,8 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIBSSH2_H 1 -/* Define to 1 if you have the `ssl' library (-lssl). */ -#cmakedefine HAVE_LIBSSL 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LIBSSH_LIBSSH_H 1 /* if zlib is available */ #cmakedefine HAVE_LIBZ 1 @@ -404,9 +430,6 @@ /* if brotli is available */ #cmakedefine HAVE_BROTLI 1 -/* if your compiler supports LL */ -#cmakedefine HAVE_LL 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LOCALE_H 1 @@ -414,7 +437,9 @@ #cmakedefine HAVE_LOCALTIME_R 1 /* Define to 1 if the compiler supports the 'long long' data type. */ -#cmakedefine HAVE_LONGLONG 1 +#if KWIML_ABI_SIZEOF_LONG_LONG +# define HAVE_LONGLONG 1 +#endif /* Define to 1 if you have the malloc.h header file. */ #cmakedefine HAVE_MALLOC_H 1 @@ -446,9 +471,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_OPENSSL_CRYPTO_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_OPENSSL_ENGINE_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_OPENSSL_ERR_H 1 @@ -575,9 +597,6 @@ /* Define to 1 if you have the `socket' function. */ #cmakedefine HAVE_SOCKET 1 -/* Define to 1 if you have the `SSL_get_shutdown' function. */ -#cmakedefine HAVE_SSL_GET_SHUTDOWN 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SSL_H 1 @@ -879,35 +898,46 @@ /* Define to the function return type for send. */ #cmakedefine SEND_TYPE_RETV ${SEND_TYPE_RETV} +/* + Note: SIZEOF_* variables are fetched with CMake through check_type_size(). + As per CMake documentation on CheckTypeSize, C preprocessor code is + generated by CMake into SIZEOF_*_CODE. This is what we use in the + following statements. + + Reference: https://cmake.org/cmake/help/latest/module/CheckTypeSize.html +*/ + /* The size of `int', as computed by sizeof. */ -@SIZEOF_INT_CODE@ +#define SIZEOF_INT KWIML_ABI_SIZEOF_INT /* The size of `short', as computed by sizeof. */ -@SIZEOF_SHORT_CODE@ +#define SIZEOF_SHORT KWIML_ABI_SIZEOF_SHORT /* The size of `long', as computed by sizeof. */ -@SIZEOF_LONG_CODE@ +#define SIZEOF_LONG KWIML_ABI_SIZEOF_LONG /* The size of `long long', as computed by sizeof. */ -@SIZEOF_LONG_LONG_CODE@ +#define SIZEOF_LONG_LONG KWIML_ABI_SIZEOF_LONG_LONG /* The size of `__int64', as computed by sizeof. */ -@SIZEOF___INT64_CODE@ +#if KWIML_ABI_SIZEOF___INT64 +# define SIZEOF___INT64 KWIML_ABI_SIZEOF___INT64 +#endif /* The size of `off_t', as computed by sizeof. */ -@SIZEOF_OFF_T_CODE@ +${SIZEOF_OFF_T_CODE} /* The size of `curl_off_t', as computed by sizeof. */ -@SIZEOF_CURL_OFF_T_CODE@ +${SIZEOF_CURL_OFF_T_CODE} /* The size of `size_t', as computed by sizeof. */ -@SIZEOF_SIZE_T_CODE@ +${SIZEOF_SIZE_T_CODE} /* The size of `ssize_t', as computed by sizeof. */ -@SIZEOF_SSIZE_T_CODE@ +${SIZEOF_SSIZE_T_CODE} /* The size of `time_t', as computed by sizeof. */ -@SIZEOF_TIME_T_CODE@ +${SIZEOF_TIME_T_CODE} /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS 1 @@ -933,15 +963,21 @@ /* if GnuTLS is enabled */ #cmakedefine USE_GNUTLS 1 -/* if PolarSSL is enabled */ -#cmakedefine USE_POLARSSL 1 - /* if Secure Transport is enabled */ #cmakedefine USE_SECTRANSP 1 /* if mbedTLS is enabled */ #cmakedefine USE_MBEDTLS 1 +/* if BearSSL is enabled */ +#cmakedefine USE_BEARSSL 1 + +/* if WolfSSL is enabled */ +#cmakedefine USE_WOLFSSL 1 + +/* if libSSH is in use */ +#cmakedefine USE_LIBSSH 1 + /* if libSSH2 is in use */ #cmakedefine USE_LIBSSH2 1 @@ -951,6 +987,9 @@ /* if NSS is enabled */ #cmakedefine USE_NSS 1 +/* if you have the PK11_CreateManagedGenericObject function */ +#cmakedefine HAVE_PK11_CREATEMANAGEDGENERICOBJECT 1 + /* if you want to use OpenLDAP code instead of legacy ldap implementation */ #cmakedefine USE_OPENLDAP 1 @@ -960,9 +999,24 @@ /* to enable NGHTTP2 */ #cmakedefine USE_NGHTTP2 1 +/* to enable NGTCP2 */ +#cmakedefine USE_NGTCP2 1 + +/* to enable NGHTTP3 */ +#cmakedefine USE_NGHTTP3 1 + +/* to enable quiche */ +#cmakedefine USE_QUICHE 1 + +/* Define to 1 if you have the quiche_conn_set_qlog_fd function. */ +#cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1 + /* if Unix domain sockets are enabled */ #cmakedefine USE_UNIX_SOCKETS +/* to enable alt-svc */ +#cmakedefine USE_ALTSVC 1 + /* to enable SSPI support */ #cmakedefine USE_WINDOWS_SSPI 1 diff --git a/Utilities/cmcurl/lib/curl_des.c b/Utilities/cmcurl/lib/curl_des.c index b123a00f01e..39c0f35ee6d 100644 --- a/Utilities/cmcurl/lib/curl_des.c +++ b/Utilities/cmcurl/lib/curl_des.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015, Steve Holme, . + * Copyright (C) 2015 - 2019, Steve Holme, . * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_des.h b/Utilities/cmcurl/lib/curl_des.h index 129060ff7de..a42eeb53f34 100644 --- a/Utilities/cmcurl/lib/curl_des.h +++ b/Utilities/cmcurl/lib/curl_des.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015, Steve Holme, . + * Copyright (C) 2015 - 2019, Steve Holme, . * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_endian.c b/Utilities/cmcurl/lib/curl_endian.c index b7563b3ded7..a774d136e49 100644 --- a/Utilities/cmcurl/lib/curl_endian.c +++ b/Utilities/cmcurl/lib/curl_endian.c @@ -81,6 +81,7 @@ unsigned short Curl_read16_be(const unsigned char *buf) ((unsigned short)buf[1])); } +#if (CURL_SIZEOF_CURL_OFF_T > 4) /* * write32_le() * @@ -100,7 +101,6 @@ static void write32_le(const int value, unsigned char *buffer) buffer[3] = (char)((value & 0xFF000000) >> 24); } -#if (CURL_SIZEOF_CURL_OFF_T > 4) /* * Curl_write64_le() * diff --git a/Utilities/cmcurl/lib/curl_endian.h b/Utilities/cmcurl/lib/curl_endian.h index 4f345a6a22c..9830e58fd70 100644 --- a/Utilities/cmcurl/lib/curl_endian.h +++ b/Utilities/cmcurl/lib/curl_endian.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,9 +31,6 @@ unsigned int Curl_read32_le(const unsigned char *buf); /* Converts a 16-bit integer from big endian */ unsigned short Curl_read16_be(const unsigned char *buf); -/* Converts a 32-bit integer to little endian */ -void Curl_write32_le(const int value, unsigned char *buffer); - #if (CURL_SIZEOF_CURL_OFF_T > 4) /* Converts a 64-bit integer to little endian */ #if defined(HAVE_LONGLONG) diff --git a/Utilities/cmcurl/lib/curl_fnmatch.h b/Utilities/cmcurl/lib/curl_fnmatch.h index 69ffe392fdb..34fccae4883 100644 --- a/Utilities/cmcurl/lib/curl_fnmatch.h +++ b/Utilities/cmcurl/lib/curl_fnmatch.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_gethostname.h b/Utilities/cmcurl/lib/curl_gethostname.h index 07517c5359c..8ae15e6c194 100644 --- a/Utilities/cmcurl/lib/curl_gethostname.h +++ b/Utilities/cmcurl/lib/curl_gethostname.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h index 756dc9e4cd2..9b70c8468b6 100644 --- a/Utilities/cmcurl/lib/curl_hmac.h +++ b/Utilities/cmcurl/lib/curl_hmac.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,6 +24,8 @@ #ifndef CURL_DISABLE_CRYPTO_AUTH +#define HMAC_MD5_LENGTH 16 + typedef void (* HMAC_hinit_func)(void *context); typedef void (* HMAC_hupdate_func)(void *context, const unsigned char *data, @@ -32,35 +34,38 @@ typedef void (* HMAC_hfinal_func)(unsigned char *result, void *context); /* Per-hash function HMAC parameters. */ - -typedef struct { - HMAC_hinit_func hmac_hinit; /* Initialize context procedure. */ +struct HMAC_params { + HMAC_hinit_func + hmac_hinit; /* Initialize context procedure. */ HMAC_hupdate_func hmac_hupdate; /* Update context with data. */ HMAC_hfinal_func hmac_hfinal; /* Get final result procedure. */ unsigned int hmac_ctxtsize; /* Context structure size. */ unsigned int hmac_maxkeylen; /* Maximum key length (bytes). */ unsigned int hmac_resultlen; /* Result length (bytes). */ -} HMAC_params; +}; /* HMAC computation context. */ - -typedef struct { - const HMAC_params *hmac_hash; /* Hash function definition. */ +struct HMAC_context { + const struct HMAC_params *hmac_hash; /* Hash function definition. */ void *hmac_hashctxt1; /* Hash function context 1. */ void *hmac_hashctxt2; /* Hash function context 2. */ -} HMAC_context; +}; /* Prototypes. */ - -HMAC_context * Curl_HMAC_init(const HMAC_params *hashparams, - const unsigned char *key, - unsigned int keylen); -int Curl_HMAC_update(HMAC_context *context, +struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams, + const unsigned char *key, + unsigned int keylen); +int Curl_HMAC_update(struct HMAC_context *context, const unsigned char *data, unsigned int len); -int Curl_HMAC_final(HMAC_context *context, unsigned char *result); +int Curl_HMAC_final(struct HMAC_context *context, unsigned char *result); + +CURLcode Curl_hmacit(const struct HMAC_params *hashparams, + const unsigned char *key, const size_t keylen, + const unsigned char *data, const size_t datalen, + unsigned char *output); #endif diff --git a/Utilities/cmcurl/lib/curl_ldap.h b/Utilities/cmcurl/lib/curl_ldap.h index 94c002948c5..912e13107c7 100644 --- a/Utilities/cmcurl/lib/curl_ldap.h +++ b/Utilities/cmcurl/lib/curl_ldap.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_md4.h b/Utilities/cmcurl/lib/curl_md4.h index 392203f9e38..c7bb2098154 100644 --- a/Utilities/cmcurl/lib/curl_md4.h +++ b/Utilities/cmcurl/lib/curl_md4.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,14 +24,13 @@ #include "curl_setup.h" -#if defined(USE_NSS) || defined(USE_OS400CRYPTO) || \ - (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) || \ - (defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C)) +#if !defined(CURL_DISABLE_CRYPTO_AUTH) -void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len); +#define MD4_DIGEST_LENGTH 16 -#endif /* defined(USE_NSS) || defined(USE_OS400CRYPTO) || - (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) || - (defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C)) */ +void Curl_md4it(unsigned char *output, const unsigned char *input, + const size_t len); + +#endif /* !defined(CURL_DISABLE_CRYPTO_AUTH) */ #endif /* HEADER_CURL_MD4_H */ diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h index aaf25f61bbd..e06c68edfcc 100644 --- a/Utilities/cmcurl/lib/curl_md5.h +++ b/Utilities/cmcurl/lib/curl_md5.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -33,30 +33,30 @@ typedef void (* Curl_MD5_update_func)(void *context, unsigned int len); typedef void (* Curl_MD5_final_func)(unsigned char *result, void *context); -typedef struct { +struct MD5_params { Curl_MD5_init_func md5_init_func; /* Initialize context procedure */ Curl_MD5_update_func md5_update_func; /* Update context with data */ Curl_MD5_final_func md5_final_func; /* Get final result procedure */ unsigned int md5_ctxtsize; /* Context structure size */ unsigned int md5_resultlen; /* Result length (bytes) */ -} MD5_params; +}; -typedef struct { - const MD5_params *md5_hash; /* Hash function definition */ +struct MD5_context { + const struct MD5_params *md5_hash; /* Hash function definition */ void *md5_hashctx; /* Hash function context */ -} MD5_context; +}; -extern const MD5_params Curl_DIGEST_MD5[1]; -extern const HMAC_params Curl_HMAC_MD5[1]; +extern const struct MD5_params Curl_DIGEST_MD5[1]; +extern const struct HMAC_params Curl_HMAC_MD5[1]; -void Curl_md5it(unsigned char *output, - const unsigned char *input); +void Curl_md5it(unsigned char *output, const unsigned char *input, + const size_t len); -MD5_context * Curl_MD5_init(const MD5_params *md5params); -CURLcode Curl_MD5_update(MD5_context *context, +struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params); +CURLcode Curl_MD5_update(struct MD5_context *context, const unsigned char *data, unsigned int len); -CURLcode Curl_MD5_final(MD5_context *context, unsigned char *result); +CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result); #endif diff --git a/Utilities/cmcurl/lib/curl_memrchr.h b/Utilities/cmcurl/lib/curl_memrchr.h index 747509c45af..90a8a07cce4 100644 --- a/Utilities/cmcurl/lib/curl_memrchr.h +++ b/Utilities/cmcurl/lib/curl_memrchr.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c index e48334faff4..2c8925b5310 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.c +++ b/Utilities/cmcurl/lib/curl_multibyte.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -20,24 +20,21 @@ * ***************************************************************************/ -#include "curl_setup.h" - -#include +/* + * This file is 'mem-include-scan' clean. See test 1132. + */ -#if defined(USE_WIN32_IDN) || ((defined(USE_WINDOWS_SSPI) || \ - defined(USE_WIN32_LDAP)) && defined(UNICODE)) +#include "curl_setup.h" - /* - * MultiByte conversions using Windows kernel32 library. - */ +#if defined(WIN32) #include "curl_multibyte.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" +/* + * MultiByte conversions using Windows kernel32 library. + */ -wchar_t *Curl_convert_UTF8_to_wchar(const char *str_utf8) +wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8) { wchar_t *str_w = NULL; @@ -59,7 +56,7 @@ wchar_t *Curl_convert_UTF8_to_wchar(const char *str_utf8) return str_w; } -char *Curl_convert_wchar_to_UTF8(const wchar_t *str_w) +char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w) { char *str_utf8 = NULL; @@ -81,4 +78,76 @@ char *Curl_convert_wchar_to_UTF8(const wchar_t *str_w) return str_utf8; } -#endif /* USE_WIN32_IDN || ((USE_WINDOWS_SSPI || USE_WIN32_LDAP) && UNICODE) */ +#endif /* WIN32 */ + +#if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES) + +FILE *curlx_win32_fopen(const char *filename, const char *mode) +{ +#ifdef _UNICODE + FILE *result = NULL; + wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename); + wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode); + if(filename_w && mode_w) + result = _wfopen(filename_w, mode_w); + free(filename_w); + free(mode_w); + if(result) + return result; +#endif + + return (fopen)(filename, mode); +} + +int curlx_win32_stat(const char *path, struct_stat *buffer) +{ + int result = -1; +#ifdef _UNICODE + wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); +#endif /* _UNICODE */ + +#if defined(USE_WIN32_SMALL_FILES) +#if defined(_UNICODE) + if(path_w) + result = _wstat(path_w, buffer); + else +#endif /* _UNICODE */ + result = _stat(path, buffer); +#else /* USE_WIN32_SMALL_FILES */ +#if defined(_UNICODE) + if(path_w) + result = _wstati64(path_w, buffer); + else +#endif /* _UNICODE */ + result = _stati64(path, buffer); +#endif /* USE_WIN32_SMALL_FILES */ + +#ifdef _UNICODE + free(path_w); +#endif + + return result; +} + +int curlx_win32_access(const char *path, int mode) +{ + int result = -1; +#ifdef _UNICODE + wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); +#endif /* _UNICODE */ + +#if defined(_UNICODE) + if(path_w) + result = _waccess(path_w, mode); + else +#endif /* _UNICODE */ + result = _access(path, mode); + +#ifdef _UNICODE + free(path_w); +#endif + + return result; +} + +#endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */ diff --git a/Utilities/cmcurl/lib/curl_multibyte.h b/Utilities/cmcurl/lib/curl_multibyte.h index 615f5c086c0..5f8c05afccb 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.h +++ b/Utilities/cmcurl/lib/curl_multibyte.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,46 +23,46 @@ ***************************************************************************/ #include "curl_setup.h" -#if defined(USE_WIN32_IDN) || ((defined(USE_WINDOWS_SSPI) || \ - defined(USE_WIN32_LDAP)) && defined(UNICODE)) +#if defined(WIN32) /* * MultiByte conversions using Windows kernel32 library. */ -wchar_t *Curl_convert_UTF8_to_wchar(const char *str_utf8); -char *Curl_convert_wchar_to_UTF8(const wchar_t *str_w); +wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8); +char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); -#endif /* USE_WIN32_IDN || ((USE_WINDOWS_SSPI || USE_WIN32_LDAP) && UNICODE) */ - - -#if defined(USE_WIN32_IDN) || defined(USE_WINDOWS_SSPI) || \ - defined(USE_WIN32_LDAP) +#endif /* WIN32 */ /* - * Macros Curl_convert_UTF8_to_tchar(), Curl_convert_tchar_to_UTF8() - * and Curl_unicodefree() main purpose is to minimize the number of + * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8() + * and curlx_unicodefree() main purpose is to minimize the number of * preprocessor conditional directives needed by code using these * to differentiate UNICODE from non-UNICODE builds. * - * When building with UNICODE defined, this two macros - * Curl_convert_UTF8_to_tchar() and Curl_convert_tchar_to_UTF8() + * When building with UNICODE defined, these two macros + * curlx_convert_UTF8_to_tchar() and curlx_convert_tchar_to_UTF8() * return a pointer to a newly allocated memory area holding result. * When the result is no longer needed, allocated memory is intended - * to be free'ed with Curl_unicodefree(). + * to be free'ed with curlx_unicodefree(). * * When building without UNICODE defined, this macros - * Curl_convert_UTF8_to_tchar() and Curl_convert_tchar_to_UTF8() - * return the pointer received as argument. Curl_unicodefree() does + * curlx_convert_UTF8_to_tchar() and curlx_convert_tchar_to_UTF8() + * return the pointer received as argument. curlx_unicodefree() does * no actual free'ing of this pointer it is simply set to NULL. */ -#ifdef UNICODE +#if defined(UNICODE) && defined(WIN32) -#define Curl_convert_UTF8_to_tchar(ptr) Curl_convert_UTF8_to_wchar((ptr)) -#define Curl_convert_tchar_to_UTF8(ptr) Curl_convert_wchar_to_UTF8((ptr)) -#define Curl_unicodefree(ptr) \ - do {if((ptr)) {free((ptr)); (ptr) = NULL;}} WHILE_FALSE +#define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr)) +#define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr)) +#define curlx_unicodefree(ptr) \ + do { \ + if(ptr) { \ + (free)(ptr); \ + (ptr) = NULL; \ + } \ + } while(0) typedef union { unsigned short *tchar_ptr; @@ -73,10 +73,10 @@ typedef union { #else -#define Curl_convert_UTF8_to_tchar(ptr) (ptr) -#define Curl_convert_tchar_to_UTF8(ptr) (ptr) -#define Curl_unicodefree(ptr) \ - do {(ptr) = NULL;} WHILE_FALSE +#define curlx_convert_UTF8_to_tchar(ptr) (ptr) +#define curlx_convert_tchar_to_UTF8(ptr) (ptr) +#define curlx_unicodefree(ptr) \ + do {(ptr) = NULL;} while(0) typedef union { char *tchar_ptr; @@ -85,8 +85,6 @@ typedef union { const unsigned char *const_tbyte_ptr; } xcharp_u; -#endif /* UNICODE */ - -#endif /* USE_WIN32_IDN || USE_WINDOWS_SSPI || USE_WIN32_LDAP */ +#endif /* UNICODE && WIN32 */ #endif /* HEADER_CURL_MULTIBYTE_H */ diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index b6df38f710b..0eefb158792 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -52,18 +52,18 @@ #if !defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO) -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_WOLFSSL) + +#ifdef USE_WOLFSSL +#include +#endif # include -# ifndef OPENSSL_NO_MD4 -# include -# else -# include "curl_md4.h" -# endif # include # include # include -# if (OPENSSL_VERSION_NUMBER < 0x00907001L) +# if (defined(OPENSSL_VERSION_NUMBER) && \ + (OPENSSL_VERSION_NUMBER < 0x00907001L)) && !defined(USE_WOLFSSL) # define DES_key_schedule des_key_schedule # define DES_cblock des_cblock # define DES_set_odd_parity des_set_odd_parity @@ -79,29 +79,21 @@ #elif defined(USE_GNUTLS_NETTLE) # include -# include #elif defined(USE_GNUTLS) # include -# define MD5_DIGEST_LENGTH 16 -# define MD4_DIGEST_LENGTH 16 #elif defined(USE_NSS) # include # include # include -# include "curl_md4.h" -# define MD5_DIGEST_LENGTH MD5_LENGTH #elif defined(USE_MBEDTLS) # include -# include -# if !defined(MBEDTLS_MD4_C) -# include "curl_md4.h" -# endif +# include "curl_md4.h" #elif defined(USE_SECTRANSP) @@ -110,7 +102,6 @@ #elif defined(USE_OS400CRYPTO) # include "cipher.mih" /* mih/cipher */ -# include "curl_md4.h" #elif defined(USE_WIN32_CRYPTO) # include #else @@ -126,12 +117,12 @@ #include "warnless.h" #include "curl_endian.h" #include "curl_des.h" +#include "curl_md4.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -#define NTLM_HMAC_MD5_LEN (16) #define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00" #define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4) @@ -150,7 +141,7 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key) key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); } -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_WOLFSSL) /* * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The * key schedule ks is also set. @@ -218,7 +209,6 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { const CK_MECHANISM_TYPE mech = CKM_DES_ECB; /* DES cipher in ECB mode */ - PK11SlotInfo *slot = NULL; char key[8]; /* expanded 64 bit key */ SECItem key_item; PK11SymKey *symkey = NULL; @@ -228,7 +218,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, bool rv = FALSE; /* use internal slot for DES encryption (requires NSS to be initialized) */ - slot = PK11_GetInternalKeySlot(); + PK11SlotInfo *slot = PK11_GetInternalKeySlot(); if(!slot) return FALSE; @@ -355,7 +345,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, /* Acquire the crypto provider */ if(!CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) return FALSE; /* Setup the key blob structure */ @@ -400,7 +390,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, const unsigned char *plaintext, unsigned char *results) { -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_WOLFSSL) DES_key_schedule ks; setup_des_key(keys, DESKEY(ks)); @@ -475,7 +465,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(struct Curl_easy *data, { /* Create LanManager hashed password. */ -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_WOLFSSL) DES_key_schedule ks; setup_des_key(pw, DESKEY(ks)); @@ -553,7 +543,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data, CURLcode result; if(len > SIZE_T_MAX/2) /* avoid integer overflow */ return CURLE_OUT_OF_MEMORY; - pw = len ? malloc(len * 2) : strdup(""); + pw = len ? malloc(len * 2) : (unsigned char *)strdup(""); if(!pw) return CURLE_OUT_OF_MEMORY; @@ -567,57 +557,10 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data, if(result) return result; - { - /* Create NT hashed password. */ -#ifdef USE_OPENSSL -#if !defined(OPENSSL_NO_MD4) - MD4_CTX MD4pw; - MD4_Init(&MD4pw); - MD4_Update(&MD4pw, pw, 2 * len); - MD4_Final(ntbuffer, &MD4pw); -#else - Curl_md4it(ntbuffer, pw, 2 * len); -#endif -#elif defined(USE_GNUTLS_NETTLE) - struct md4_ctx MD4pw; - md4_init(&MD4pw); - md4_update(&MD4pw, (unsigned int)(2 * len), pw); - md4_digest(&MD4pw, MD4_DIGEST_SIZE, ntbuffer); -#elif defined(USE_GNUTLS) - gcry_md_hd_t MD4pw; - gcry_md_open(&MD4pw, GCRY_MD_MD4, 0); - gcry_md_write(MD4pw, pw, 2 * len); - memcpy(ntbuffer, gcry_md_read(MD4pw, 0), MD4_DIGEST_LENGTH); - gcry_md_close(MD4pw); -#elif defined(USE_NSS) - Curl_md4it(ntbuffer, pw, 2 * len); -#elif defined(USE_MBEDTLS) -#if defined(MBEDTLS_MD4_C) - mbedtls_md4(pw, 2 * len, ntbuffer); -#else - Curl_md4it(ntbuffer, pw, 2 * len); -#endif -#elif defined(USE_SECTRANSP) - (void)CC_MD4(pw, (CC_LONG)(2 * len), ntbuffer); -#elif defined(USE_OS400CRYPTO) - Curl_md4it(ntbuffer, pw, 2 * len); -#elif defined(USE_WIN32_CRYPTO) - HCRYPTPROV hprov; - if(CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - HCRYPTHASH hhash; - if(CryptCreateHash(hprov, CALG_MD4, 0, 0, &hhash)) { - DWORD length = 16; - CryptHashData(hhash, pw, (unsigned int)len * 2, 0); - CryptGetHashParam(hhash, HP_HASHVAL, ntbuffer, &length, 0); - CryptDestroyHash(hhash); - } - CryptReleaseContext(hprov, 0); - } -#endif + /* Create NT hashed password. */ + Curl_md4it(ntbuffer, pw, 2 * len); - memset(ntbuffer + 16, 0, 21 - 16); - } + memset(ntbuffer + 16, 0, 21 - 16); free(pw); @@ -626,25 +569,6 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data, #if defined(USE_NTLM_V2) && !defined(USE_WINDOWS_SSPI) -/* This returns the HMAC MD5 digest */ -static CURLcode hmac_md5(const unsigned char *key, unsigned int keylen, - const unsigned char *data, unsigned int datalen, - unsigned char *output) -{ - HMAC_context *ctxt = Curl_HMAC_init(Curl_HMAC_MD5, key, keylen); - - if(!ctxt) - return CURLE_OUT_OF_MEMORY; - - /* Update the digest with the given challenge */ - Curl_HMAC_update(ctxt, data, datalen); - - /* Finalise the digest */ - Curl_HMAC_final(ctxt, output); - - return CURLE_OK; -} - /* This creates the NTLMv2 hash by using NTLM hash as the key and Unicode * (uppercase UserName + Domain) as the data */ @@ -674,8 +598,8 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, ascii_uppercase_to_unicode_le(identity, user, userlen); ascii_to_unicode_le(identity + (userlen << 1), domain, domlen); - result = hmac_md5(ntlmhash, 16, identity, curlx_uztoui(identity_len), - ntlmv2hash); + result = Curl_hmacit(Curl_HMAC_MD5, ntlmhash, 16, identity, identity_len, + ntlmv2hash); free(identity); return result; @@ -721,7 +645,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, unsigned int len = 0; unsigned char *ptr = NULL; - unsigned char hmac_output[NTLM_HMAC_MD5_LEN]; + unsigned char hmac_output[HMAC_MD5_LENGTH]; curl_off_t tw; CURLcode result = CURLE_OK; @@ -740,7 +664,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, tw = ((curl_off_t)time(NULL) + CURL_OFF_T_C(11644473600)) * 10000000; /* Calculate the response len */ - len = NTLM_HMAC_MD5_LEN + NTLMv2_BLOB_LEN; + len = HMAC_MD5_LENGTH + NTLMv2_BLOB_LEN; /* Allocate the response */ ptr = calloc(1, len); @@ -748,7 +672,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, return CURLE_OUT_OF_MEMORY; /* Create the BLOB structure */ - msnprintf((char *)ptr + NTLM_HMAC_MD5_LEN, NTLMv2_BLOB_LEN, + msnprintf((char *)ptr + HMAC_MD5_LENGTH, NTLMv2_BLOB_LEN, "%c%c%c%c" /* NTLMv2_BLOB_SIGNATURE */ "%c%c%c%c", /* Reserved = 0 */ NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1], @@ -761,7 +685,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, /* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */ memcpy(ptr + 8, &ntlm->nonce[0], 8); - result = hmac_md5(ntlmv2hash, NTLM_HMAC_MD5_LEN, ptr + 8, + result = Curl_hmacit(Curl_HMAC_MD5, ntlmv2hash, HMAC_MD5_LENGTH, ptr + 8, NTLMv2_BLOB_LEN + 8, hmac_output); if(result) { free(ptr); @@ -769,7 +693,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, } /* Concatenate the HMAC MD5 output with the BLOB */ - memcpy(ptr, hmac_output, NTLM_HMAC_MD5_LEN); + memcpy(ptr, hmac_output, HMAC_MD5_LENGTH); /* Return the response */ *ntresp = ptr; @@ -804,7 +728,8 @@ CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, memcpy(&data[0], challenge_server, 8); memcpy(&data[8], challenge_client, 8); - result = hmac_md5(ntlmv2hash, 16, &data[0], 16, hmac_output); + result = Curl_hmacit(Curl_HMAC_MD5, ntlmv2hash, 16, &data[0], 16, + hmac_output); if(result) return result; diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.h b/Utilities/cmcurl/lib/curl_ntlm_core.h index 3b4b8053c3e..7895b647095 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.h +++ b/Utilities/cmcurl/lib/curl_ntlm_core.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,6 +29,7 @@ /* If NSS is the first available SSL backend (see order in curl_ntlm_core.c) then it must be initialized to be used by NTLM. */ #if !defined(USE_OPENSSL) && \ + !defined(USE_WOLFSSL) && \ !defined(USE_GNUTLS_NETTLE) && \ !defined(USE_GNUTLS) && \ defined(USE_NSS) @@ -37,7 +38,10 @@ #if !defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO) -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_WOLFSSL) +#ifdef USE_WOLFSSL +# include +#endif # include #endif @@ -46,9 +50,9 @@ #define USE_NTRESPONSES /* Define USE_NTLM2SESSION in order to make the type-3 message include the - NTLM2Session response message, requires USE_NTRESPONSES defined to 1 and a - Crypto engine that we have curl_ssl_md5sum() for. */ -#if defined(USE_NTRESPONSES) && !defined(USE_WIN32_CRYPTO) + NTLM2Session response message, requires USE_NTRESPONSES defined to 1 and + MD5 support */ +#if defined(USE_NTRESPONSES) && !defined(CURL_DISABLE_CRYPTO_AUTH) #define USE_NTLM2SESSION #endif diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c index 80266e2a457..17a92f8ca25 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_wb.c +++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -76,22 +76,22 @@ # define sclose_nolog(x) close((x)) #endif -void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn) +static void ntlm_wb_cleanup(struct ntlmdata *ntlm) { - if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) { - sclose(conn->ntlm_auth_hlpr_socket); - conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; + if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) { + sclose(ntlm->ntlm_auth_hlpr_socket); + ntlm->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; } - if(conn->ntlm_auth_hlpr_pid) { + if(ntlm->ntlm_auth_hlpr_pid) { int i; for(i = 0; i < 4; i++) { - pid_t ret = waitpid(conn->ntlm_auth_hlpr_pid, NULL, WNOHANG); - if(ret == conn->ntlm_auth_hlpr_pid || errno == ECHILD) + pid_t ret = waitpid(ntlm->ntlm_auth_hlpr_pid, NULL, WNOHANG); + if(ret == ntlm->ntlm_auth_hlpr_pid || errno == ECHILD) break; switch(i) { case 0: - kill(conn->ntlm_auth_hlpr_pid, SIGTERM); + kill(ntlm->ntlm_auth_hlpr_pid, SIGTERM); break; case 1: /* Give the process another moment to shut down cleanly before @@ -99,22 +99,21 @@ void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn) Curl_wait_ms(1); break; case 2: - kill(conn->ntlm_auth_hlpr_pid, SIGKILL); + kill(ntlm->ntlm_auth_hlpr_pid, SIGKILL); break; case 3: break; } } - conn->ntlm_auth_hlpr_pid = 0; + ntlm->ntlm_auth_hlpr_pid = 0; } - free(conn->challenge_header); - conn->challenge_header = NULL; - free(conn->response_header); - conn->response_header = NULL; + Curl_safefree(ntlm->challenge); + Curl_safefree(ntlm->response); } -static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) +static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm, + const char *userp) { curl_socket_t sockfds[2]; pid_t child_pid; @@ -128,9 +127,13 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) #endif char buffer[STRERROR_LEN]; +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) data; +#endif + /* Return if communication with ntlm_auth already set up */ - if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD || - conn->ntlm_auth_hlpr_pid) + if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD || + ntlm->ntlm_auth_hlpr_pid) return CURLE_OK; username = userp; @@ -181,13 +184,13 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) ntlm_auth = NTLM_WB_FILE; if(access(ntlm_auth, X_OK) != 0) { - failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s", + failf(data, "Could not access ntlm_auth: %s errno %d: %s", ntlm_auth, errno, Curl_strerror(errno, buffer, sizeof(buffer))); goto done; } - if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) { - failf(conn->data, "Could not open socket pair. errno %d: %s", + if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) { + failf(data, "Could not open socket pair. errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); goto done; } @@ -196,7 +199,7 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) if(child_pid == -1) { sclose(sockfds[0]); sclose(sockfds[1]); - failf(conn->data, "Could not fork. errno %d: %s", + failf(data, "Could not fork. errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); goto done; } @@ -208,13 +211,13 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) /* Don't use sclose in the child since it fools the socket leak detector */ sclose_nolog(sockfds[0]); if(dup2(sockfds[1], STDIN_FILENO) == -1) { - failf(conn->data, "Could not redirect child stdin. errno %d: %s", + failf(data, "Could not redirect child stdin. errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); exit(1); } if(dup2(sockfds[1], STDOUT_FILENO) == -1) { - failf(conn->data, "Could not redirect child stdout. errno %d: %s", + failf(data, "Could not redirect child stdout. errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); exit(1); } @@ -234,14 +237,14 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) NULL); sclose_nolog(sockfds[1]); - failf(conn->data, "Could not execl(). errno %d: %s", + failf(data, "Could not execl(). errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); exit(1); } sclose(sockfds[1]); - conn->ntlm_auth_hlpr_socket = sockfds[0]; - conn->ntlm_auth_hlpr_pid = child_pid; + ntlm->ntlm_auth_hlpr_socket = sockfds[0]; + ntlm->ntlm_auth_hlpr_pid = child_pid; free(domain); free(ntlm_auth_alloc); return CURLE_OK; @@ -255,17 +258,17 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) /* if larger than this, something is seriously wrong */ #define MAX_NTLM_WB_RESPONSE 100000 -static CURLcode ntlm_wb_response(struct connectdata *conn, +static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm, const char *input, curlntlm state) { - char *buf = malloc(NTLM_BUFSIZE); size_t len_in = strlen(input), len_out = 0; - - if(!buf) - return CURLE_OUT_OF_MEMORY; + struct dynbuf b; + char *ptr = NULL; + unsigned char *buf = (unsigned char *)data->state.buffer; + Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE); while(len_in > 0) { - ssize_t written = swrite(conn->ntlm_auth_hlpr_socket, input, len_in); + ssize_t written = swrite(ntlm->ntlm_auth_hlpr_socket, input, len_in); if(written == -1) { /* Interrupted by a signal, retry it */ if(errno == EINTR) @@ -278,10 +281,8 @@ static CURLcode ntlm_wb_response(struct connectdata *conn, } /* Read one line */ while(1) { - ssize_t size; - char *newbuf; - - size = sread(conn->ntlm_auth_hlpr_socket, buf + len_out, NTLM_BUFSIZE); + ssize_t size = + sread(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size); if(size == -1) { if(errno == EINTR) continue; @@ -290,48 +291,41 @@ static CURLcode ntlm_wb_response(struct connectdata *conn, else if(size == 0) goto done; - len_out += size; - if(buf[len_out - 1] == '\n') { - buf[len_out - 1] = '\0'; - break; - } + if(Curl_dyn_addn(&b, buf, size)) + goto done; - if(len_out > MAX_NTLM_WB_RESPONSE) { - failf(conn->data, "too large ntlm_wb response!"); - free(buf); - return CURLE_OUT_OF_MEMORY; + len_out = Curl_dyn_len(&b); + ptr = Curl_dyn_ptr(&b); + if(len_out && ptr[len_out - 1] == '\n') { + ptr[len_out - 1] = '\0'; + break; /* done! */ } - - newbuf = Curl_saferealloc(buf, len_out + NTLM_BUFSIZE); - if(!newbuf) - return CURLE_OUT_OF_MEMORY; - - buf = newbuf; + /* loop */ } /* Samba/winbind installed but not configured */ if(state == NTLMSTATE_TYPE1 && len_out == 3 && - buf[0] == 'P' && buf[1] == 'W') + ptr[0] == 'P' && ptr[1] == 'W') goto done; /* invalid response */ if(len_out < 4) goto done; if(state == NTLMSTATE_TYPE1 && - (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' ')) + (ptr[0]!='Y' || ptr[1]!='R' || ptr[2]!=' ')) goto done; if(state == NTLMSTATE_TYPE2 && - (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') && - (buf[0]!='A' || buf[1]!='F' || buf[2]!=' ')) + (ptr[0]!='K' || ptr[1]!='K' || ptr[2]!=' ') && + (ptr[0]!='A' || ptr[1]!='F' || ptr[2]!=' ')) goto done; - conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3); - free(buf); - if(!conn->response_header) + ntlm->response = strdup(ptr + 3); + Curl_dyn_free(&b); + if(!ntlm->response) return CURLE_OUT_OF_MEMORY; return CURLE_OK; done: - free(buf); + Curl_dyn_free(&b); return CURLE_REMOTE_ACCESS_DENIED; } @@ -339,6 +333,7 @@ CURLcode Curl_input_ntlm_wb(struct connectdata *conn, bool proxy, const char *header) { + struct ntlmdata *ntlm = proxy ? &conn->proxyntlm : &conn->ntlm; curlntlm *state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state; if(!checkprefix("NTLM", header)) @@ -349,8 +344,8 @@ CURLcode Curl_input_ntlm_wb(struct connectdata *conn, header++; if(*header) { - conn->challenge_header = strdup(header); - if(!conn->challenge_header) + ntlm->challenge = strdup(header); + if(!ntlm->challenge) return CURLE_OUT_OF_MEMORY; *state = NTLMSTATE_TYPE2; /* We got a type-2 message */ @@ -381,32 +376,38 @@ CURLcode Curl_input_ntlm_wb(struct connectdata *conn, * This is for creating ntlm header output by delegating challenge/response * to Samba's winbind daemon helper ntlm_auth. */ -CURLcode Curl_output_ntlm_wb(struct connectdata *conn, - bool proxy) +CURLcode Curl_output_ntlm_wb(struct connectdata *conn, bool proxy) { /* point to the address of the pointer that holds the string to send to the server, which is for a plain host or for a HTTP proxy */ char **allocuserpwd; /* point to the name and password for this */ const char *userp; + struct ntlmdata *ntlm; curlntlm *state; struct auth *authp; + struct Curl_easy *data = conn->data; CURLcode res = CURLE_OK; - char *input; DEBUGASSERT(conn); DEBUGASSERT(conn->data); if(proxy) { - allocuserpwd = &conn->allocptr.proxyuserpwd; +#ifndef CURL_DISABLE_PROXY + allocuserpwd = &data->state.aptr.proxyuserpwd; userp = conn->http_proxy.user; + ntlm = &conn->proxyntlm; state = &conn->proxy_ntlm_state; authp = &conn->data->state.authproxy; +#else + return CURLE_NOT_BUILT_IN; +#endif } else { - allocuserpwd = &conn->allocptr.userpwd; + allocuserpwd = &data->state.aptr.userpwd; userp = conn->user; + ntlm = &conn->ntlm; state = &conn->http_ntlm_state; authp = &conn->data->state.authhost; } @@ -432,38 +433,36 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, * request handling process. */ /* Create communication with ntlm_auth */ - res = ntlm_wb_init(conn, userp); + res = ntlm_wb_init(conn->data, ntlm, userp); if(res) return res; - res = ntlm_wb_response(conn, "YR\n", *state); + res = ntlm_wb_response(conn->data, ntlm, "YR\n", *state); if(res) return res; free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: %s\r\n", + *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", proxy ? "Proxy-" : "", - conn->response_header); + ntlm->response); DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); - free(conn->response_header); + Curl_safefree(ntlm->response); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; - conn->response_header = NULL; break; - case NTLMSTATE_TYPE2: - input = aprintf("TT %s\n", conn->challenge_header); + case NTLMSTATE_TYPE2: { + char *input = aprintf("TT %s\n", ntlm->challenge); if(!input) return CURLE_OUT_OF_MEMORY; - res = ntlm_wb_response(conn, input, *state); + res = ntlm_wb_response(conn->data, ntlm, input, *state); free(input); - input = NULL; if(res) return res; free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: %s\r\n", + *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", proxy ? "Proxy-" : "", - conn->response_header); + ntlm->response); DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd)); *state = NTLMSTATE_TYPE3; /* we sent a type-3 */ authp->done = TRUE; @@ -471,7 +470,7 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; break; - + } case NTLMSTATE_TYPE3: /* connection is already authenticated, * don't send a header in future requests */ @@ -486,4 +485,10 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, return CURLE_OK; } +void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn) +{ + ntlm_wb_cleanup(&conn->ntlm); + ntlm_wb_cleanup(&conn->proxyntlm); +} + #endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */ diff --git a/Utilities/cmcurl/lib/curl_path.c b/Utilities/cmcurl/lib/curl_path.c index 85dddcef1b4..fbd98cb39b4 100644 --- a/Utilities/cmcurl/lib/curl_path.c +++ b/Utilities/cmcurl/lib/curl_path.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -42,7 +42,7 @@ CURLcode Curl_getworkingpath(struct connectdata *conn, size_t working_path_len; CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &working_path, - &working_path_len, FALSE); + &working_path_len, REJECT_ZERO); if(result) return result; @@ -55,7 +55,7 @@ CURLcode Curl_getworkingpath(struct connectdata *conn, } if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) /* It is referenced to the home directory, so strip the leading '/~/' */ - memcpy(real_path, working_path + 3, 4 + working_path_len-3); + memcpy(real_path, working_path + 3, working_path_len - 2); else memcpy(real_path, working_path, 1 + working_path_len); } diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c index 16b1de1ae48..df8f2b1d95d 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.c +++ b/Utilities/cmcurl/lib/curl_rtmp.c @@ -199,13 +199,13 @@ static CURLcode rtmp_setup_connection(struct connectdata *conn) RTMP_Free(r); return CURLE_URL_MALFORMAT; } - conn->proto.generic = r; + conn->proto.rtmp = r; return CURLE_OK; } static CURLcode rtmp_connect(struct connectdata *conn, bool *done) { - RTMP *r = conn->proto.generic; + RTMP *r = conn->proto.rtmp; SET_RCVTIMEO(tv, 10); r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET]; @@ -240,7 +240,7 @@ static CURLcode rtmp_connect(struct connectdata *conn, bool *done) static CURLcode rtmp_do(struct connectdata *conn, bool *done) { struct Curl_easy *data = conn->data; - RTMP *r = conn->proto.generic; + RTMP *r = conn->proto.rtmp; if(!RTMP_ConnectStream(r, 0)) return CURLE_FAILED_INIT; @@ -268,10 +268,10 @@ static CURLcode rtmp_done(struct connectdata *conn, CURLcode status, static CURLcode rtmp_disconnect(struct connectdata *conn, bool dead_connection) { - RTMP *r = conn->proto.generic; + RTMP *r = conn->proto.rtmp; (void)dead_connection; if(r) { - conn->proto.generic = NULL; + conn->proto.rtmp = NULL; RTMP_Close(r); RTMP_Free(r); } @@ -281,7 +281,7 @@ static CURLcode rtmp_disconnect(struct connectdata *conn, static ssize_t rtmp_recv(struct connectdata *conn, int sockindex, char *buf, size_t len, CURLcode *err) { - RTMP *r = conn->proto.generic; + RTMP *r = conn->proto.rtmp; ssize_t nread; (void)sockindex; /* unused */ @@ -302,7 +302,7 @@ static ssize_t rtmp_recv(struct connectdata *conn, int sockindex, char *buf, static ssize_t rtmp_send(struct connectdata *conn, int sockindex, const void *buf, size_t len, CURLcode *err) { - RTMP *r = conn->proto.generic; + RTMP *r = conn->proto.rtmp; ssize_t num; (void)sockindex; /* unused */ diff --git a/Utilities/cmcurl/lib/curl_rtmp.h b/Utilities/cmcurl/lib/curl_rtmp.h index 3306e220059..86a01382da0 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.h +++ b/Utilities/cmcurl/lib/curl_rtmp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2010, Howard Chu, + * Copyright (C) 2010 - 2019, Howard Chu, * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index 018e4228b3c..83fe896e357 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -264,14 +264,20 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, size_t len = 0; saslstate state1 = SASL_STOP; saslstate state2 = SASL_FINAL; +#ifndef CURL_DISABLE_PROXY const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; +#else + const char * const hostname = conn->host.name; + const long int port = conn->remote_port; +#endif #if defined(USE_KERBEROS5) || defined(USE_NTLM) const char *service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : sasl->params->service; #endif + const char *oauth_bearer = data->set.str[STRING_BEARER]; sasl->force_ir = force_ir; /* Latch for future use */ sasl->authused = 0; /* No mechanism used yet */ @@ -341,7 +347,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, } else #endif - if((enabledmechs & SASL_MECH_OAUTHBEARER) && conn->oauth_bearer) { + if((enabledmechs & SASL_MECH_OAUTHBEARER) && oauth_bearer) { mech = SASL_MECH_STRING_OAUTHBEARER; state1 = SASL_OAUTH2; state2 = SASL_OAUTH2_RESP; @@ -351,17 +357,17 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, result = Curl_auth_create_oauth_bearer_message(data, conn->user, hostname, port, - conn->oauth_bearer, + oauth_bearer, &resp, &len); } - else if((enabledmechs & SASL_MECH_XOAUTH2) && conn->oauth_bearer) { + else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) { mech = SASL_MECH_STRING_XOAUTH2; state1 = SASL_OAUTH2; sasl->authused = SASL_MECH_XOAUTH2; if(force_ir || data->set.sasl_ir) result = Curl_auth_create_xoauth_bearer_message(data, conn->user, - conn->oauth_bearer, + oauth_bearer, &resp, &len); } else if(enabledmechs & SASL_MECH_PLAIN) { @@ -370,8 +376,9 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, sasl->authused = SASL_MECH_PLAIN; if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_plain_message(data, NULL, conn->user, - conn->passwd, &resp, &len); + result = Curl_auth_create_plain_message(data, conn->sasl_authzid, + conn->user, conn->passwd, + &resp, &len); } else if(enabledmechs & SASL_MECH_LOGIN) { mech = SASL_MECH_STRING_LOGIN; @@ -415,21 +422,27 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, struct Curl_easy *data = conn->data; saslstate newstate = SASL_FINAL; char *resp = NULL; +#ifndef CURL_DISABLE_PROXY const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; +#else + const char * const hostname = conn->host.name; + const long int port = conn->remote_port; +#endif #if !defined(CURL_DISABLE_CRYPTO_AUTH) char *chlg = NULL; size_t chlglen = 0; #endif -#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ - defined(USE_NTLM) +#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ + defined(USE_NTLM) const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sasl->params->service; + data->set.str[STRING_SERVICE_NAME] : + sasl->params->service; char *serverdata; #endif size_t len = 0; + const char *oauth_bearer = data->set.str[STRING_BEARER]; *progress = SASL_INPROGRESS; @@ -453,8 +466,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, *progress = SASL_DONE; return result; case SASL_PLAIN: - result = Curl_auth_create_plain_message(data, NULL, conn->user, - conn->passwd, &resp, &len); + result = Curl_auth_create_plain_message(data, conn->sasl_authzid, + conn->user, conn->passwd, + &resp, &len); break; case SASL_LOGIN: result = Curl_auth_create_login_message(data, conn->user, &resp, &len); @@ -556,7 +570,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, result = Curl_auth_create_oauth_bearer_message(data, conn->user, hostname, port, - conn->oauth_bearer, + oauth_bearer, &resp, &len); /* Failures maybe sent by the server as continuations for OAUTHBEARER */ @@ -564,7 +578,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, } else result = Curl_auth_create_xoauth_bearer_message(data, conn->user, - conn->oauth_bearer, + oauth_bearer, &resp, &len); break; diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index efba5ddec3f..dadfff94f17 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,14 @@ #define CURL_NO_OLDIES #endif +/* + * Disable Visual Studio warnings: + * 4127 "conditional expression is constant" + */ +#ifdef _MSC_VER +#pragma warning(disable:4127) +#endif + /* * Define WIN32 when build target is Win32 API */ @@ -96,6 +104,10 @@ # include "config-vxworks.h" #endif +#ifdef __PLAN9__ +# include "config-plan9.h" +#endif + #endif /* HAVE_CONFIG_H */ #if defined(_MSC_VER) @@ -223,64 +235,20 @@ #endif /* - * Use getaddrinfo to resolve the IPv4 address literal. If the current network - * interface doesn't support IPv4, but supports IPv6, NAT64, and DNS64, - * performing this task will result in a synthesized IPv6 address. - */ -#ifdef __APPLE__ -#define USE_RESOLVE_ON_IPS 1 -#endif - -/* - * Include header files for windows builds before redefining anything. - * Use this preprocessor block only to include or exclude windows.h, - * winsock2.h, ws2tcpip.h or winsock.h. Any other windows thing belongs - * to any other further and independent block. Under Cygwin things work - * just as under linux (e.g. ) and the winsock headers should - * never be included when __CYGWIN__ is defined. configure script takes - * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK_H, HAVE_WINSOCK2_H, - * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined. + * Windows setup file includes some system headers. */ #ifdef HAVE_WINDOWS_H -# if defined(UNICODE) && !defined(_UNICODE) -# define _UNICODE -# endif -# if defined(_UNICODE) && !defined(UNICODE) -# define UNICODE -# endif -# include -# include -# ifdef HAVE_WINSOCK2_H -# include -# ifdef HAVE_WS2TCPIP_H -# include -# endif -# else -# ifdef HAVE_WINSOCK_H -# include -# endif -# endif -# include -# ifdef UNICODE - typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str); -# endif +# include "setup-win32.h" #endif /* - * Define USE_WINSOCK to 2 if we have and use WINSOCK2 API, else - * define USE_WINSOCK to 1 if we have and use WINSOCK API, else - * undefine USE_WINSOCK. + * Use getaddrinfo to resolve the IPv4 address literal. If the current network + * interface doesn't support IPv4, but supports IPv6, NAT64, and DNS64, + * performing this task will result in a synthesized IPv6 address. */ - -#undef USE_WINSOCK - -#ifdef HAVE_WINSOCK2_H -# define USE_WINSOCK 2 -#else -# ifdef HAVE_WINSOCK_H -# define USE_WINSOCK 1 -# endif +#ifdef __APPLE__ +#define USE_RESOLVE_ON_IPS 1 #endif #ifdef USE_LWIPSOCK @@ -378,9 +346,14 @@ # undef fstat # define fstat(fdes,stp) _fstati64(fdes, stp) # undef stat -# define stat(fname,stp) _stati64(fname, stp) +# define stat(fname,stp) curlx_win32_stat(fname, stp) # define struct_stat struct _stati64 # define LSEEK_ERROR (__int64)-1 +# define fopen(fname,mode) curlx_win32_fopen(fname, mode) +# define access(fname,mode) curlx_win32_access(fname, mode) + int curlx_win32_stat(const char *path, struct_stat *buffer); + FILE *curlx_win32_fopen(const char *filename, const char *mode); + int curlx_win32_access(const char *path, int mode); #endif /* @@ -395,8 +368,13 @@ # undef lseek # define lseek(fdes,offset,whence) _lseek(fdes, (long)offset, whence) # define fstat(fdes,stp) _fstat(fdes, stp) -# define stat(fname,stp) _stat(fname, stp) +# define stat(fname,stp) curlx_win32_stat(fname, stp) # define struct_stat struct _stat +# define fopen(fname,mode) curlx_win32_fopen(fname, mode) +# define access(fname,mode) curlx_win32_access(fname, mode) + int curlx_win32_stat(const char *path, struct_stat *buffer); + FILE *curlx_win32_fopen(const char *filename, const char *mode); + int curlx_win32_access(const char *path, int mode); # endif # define LSEEK_ERROR (long)-1 #endif @@ -497,7 +475,6 @@ #ifdef WIN32 # define DIR_CHAR "\\" -# define DOT_CHAR "_" #else /* WIN32 */ @@ -523,14 +500,6 @@ # endif # define DIR_CHAR "/" -# ifndef DOT_CHAR -# define DOT_CHAR "." -# endif - -# ifdef MSDOS -# undef DOT_CHAR -# define DOT_CHAR "_" -# endif # ifndef fileno /* sunos 4 have this as a macro! */ int fileno(FILE *stream); @@ -583,6 +552,12 @@ * Mutually exclusive CURLRES_* definitions. */ +#if defined(ENABLE_IPV6) && defined(HAVE_GETADDRINFO) +# define CURLRES_IPV6 +#else +# define CURLRES_IPV4 +#endif + #ifdef USE_ARES # define CURLRES_ASYNCH # define CURLRES_ARES @@ -597,12 +572,6 @@ # define CURLRES_SYNCH #endif -#ifdef ENABLE_IPV6 -# define CURLRES_IPV6 -#else -# define CURLRES_IPV4 -#endif - /* ---------------------------------------------------------------- */ /* @@ -662,9 +631,10 @@ int netware_init(void); #define LIBIDN_REQUIRED_VERSION "0.4.1" #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \ - defined(USE_POLARSSL) || defined(USE_MBEDTLS) || \ - defined(USE_CYASSL) || defined(USE_SCHANNEL) || \ - defined(USE_SECTRANSP) || defined(USE_GSKIT) || defined(USE_MESALINK) + defined(USE_MBEDTLS) || \ + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ + defined(USE_SECTRANSP) || defined(USE_GSKIT) || defined(USE_MESALINK) || \ + defined(USE_BEARSSL) #define USE_SSL /* SSL support has been enabled */ #endif @@ -682,10 +652,11 @@ int netware_init(void); /* Single point where USE_NTLM definition might be defined */ #if !defined(CURL_DISABLE_NTLM) && !defined(CURL_DISABLE_CRYPTO_AUTH) -#if defined(USE_OPENSSL) || defined(USE_WINDOWS_SSPI) || \ - defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ - defined(USE_MBEDTLS) +#if defined(USE_OPENSSL) || defined(USE_WINDOWS_SSPI) || \ + defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ + defined(USE_MBEDTLS) || \ + (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_SET_ODD_PARITY)) #define USE_NTLM @@ -733,7 +704,7 @@ int netware_init(void); */ #ifndef Curl_nop_stmt -# define Curl_nop_stmt do { } WHILE_FALSE +# define Curl_nop_stmt do { } while(0) #endif /* @@ -842,4 +813,8 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #define UNITTEST static #endif +#if defined(USE_NGTCP2) || defined(USE_QUICHE) +#define ENABLE_QUIC +#endif + #endif /* HEADER_CURL_SETUP_H */ diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h index 413ccea9173..e7c00deabdd 100644 --- a/Utilities/cmcurl/lib/curl_setup_once.h +++ b/Utilities/cmcurl/lib/curl_setup_once.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -329,27 +329,6 @@ struct timeval { #include "curl_ctype.h" -/* - * Macro WHILE_FALSE may be used to build single-iteration do-while loops, - * avoiding compiler warnings. Mostly intended for other macro definitions. - */ - -#define WHILE_FALSE while(0) - -#if defined(_MSC_VER) && !defined(__POCC__) -# undef WHILE_FALSE -# if (_MSC_VER < 1500) -# define WHILE_FALSE while(1, 0) -# else -# define WHILE_FALSE \ -__pragma(warning(push)) \ -__pragma(warning(disable:4127)) \ -while(0) \ -__pragma(warning(pop)) -# endif -#endif - - /* * Typedef to 'int' if sig_atomic_t is not an available 'typedefed' type. */ @@ -387,7 +366,7 @@ typedef int sig_atomic_t; #ifdef DEBUGBUILD #define DEBUGF(x) x #else -#define DEBUGF(x) do { } WHILE_FALSE +#define DEBUGF(x) do { } while(0) #endif @@ -395,10 +374,11 @@ typedef int sig_atomic_t; * Macro used to include assertion code only in debug builds. */ +#undef DEBUGASSERT #if defined(DEBUGBUILD) && defined(HAVE_ASSERT_H) #define DEBUGASSERT(x) assert(x) #else -#define DEBUGASSERT(x) do { } WHILE_FALSE +#define DEBUGASSERT(x) do { } while(0) #endif @@ -501,6 +481,8 @@ typedef int sig_atomic_t; #ifdef __VMS #define argv_item_t __char_ptr32 +#elif defined(_UNICODE) +#define argv_item_t wchar_t * #else #define argv_item_t char * #endif diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h index 6db4b04dbb8..35d286cebda 100644 --- a/Utilities/cmcurl/lib/curl_sha256.h +++ b/Utilities/cmcurl/lib/curl_sha256.h @@ -7,7 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Florin Petriuc, + * Copyright (C) 2017, Florin Petriuc, + * Copyright (C) 2018 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,8 +25,10 @@ #ifndef CURL_DISABLE_CRYPTO_AUTH -void Curl_sha256it(unsigned char *outbuffer, - const unsigned char *input); +#define SHA256_DIGEST_LENGTH 32 + +void Curl_sha256it(unsigned char *outbuffer, const unsigned char *input, + const size_t len); #endif diff --git a/Utilities/cmcurl/lib/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c index 1d0de4ed33d..83ece9afdca 100644 --- a/Utilities/cmcurl/lib/curl_sspi.c +++ b/Utilities/cmcurl/lib/curl_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -151,7 +151,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, /* Initialize the identity */ memset(identity, 0, sizeof(*identity)); - useranddomain.tchar_ptr = Curl_convert_UTF8_to_tchar((char *)userp); + useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *)userp); if(!useranddomain.tchar_ptr) return CURLE_OUT_OF_MEMORY; @@ -173,7 +173,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, /* Setup the identity's user and length */ dup_user.tchar_ptr = _tcsdup(user.tchar_ptr); if(!dup_user.tchar_ptr) { - Curl_unicodefree(useranddomain.tchar_ptr); + curlx_unicodefree(useranddomain.tchar_ptr); return CURLE_OUT_OF_MEMORY; } identity->User = dup_user.tbyte_ptr; @@ -183,7 +183,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, /* Setup the identity's domain and length */ dup_domain.tchar_ptr = malloc(sizeof(TCHAR) * (domlen + 1)); if(!dup_domain.tchar_ptr) { - Curl_unicodefree(useranddomain.tchar_ptr); + curlx_unicodefree(useranddomain.tchar_ptr); return CURLE_OUT_OF_MEMORY; } _tcsncpy(dup_domain.tchar_ptr, domain.tchar_ptr, domlen); @@ -192,22 +192,22 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, identity->DomainLength = curlx_uztoul(domlen); dup_domain.tchar_ptr = NULL; - Curl_unicodefree(useranddomain.tchar_ptr); + curlx_unicodefree(useranddomain.tchar_ptr); /* Setup the identity's password and length */ - passwd.tchar_ptr = Curl_convert_UTF8_to_tchar((char *)passwdp); + passwd.tchar_ptr = curlx_convert_UTF8_to_tchar((char *)passwdp); if(!passwd.tchar_ptr) return CURLE_OUT_OF_MEMORY; dup_passwd.tchar_ptr = _tcsdup(passwd.tchar_ptr); if(!dup_passwd.tchar_ptr) { - Curl_unicodefree(passwd.tchar_ptr); + curlx_unicodefree(passwd.tchar_ptr); return CURLE_OUT_OF_MEMORY; } identity->Password = dup_passwd.tbyte_ptr; identity->PasswordLength = curlx_uztoul(_tcslen(dup_passwd.tchar_ptr)); dup_passwd.tchar_ptr = NULL; - Curl_unicodefree(passwd.tchar_ptr); + curlx_unicodefree(passwd.tchar_ptr); /* Setup the identity's flags */ identity->Flags = SECFLAG_WINNT_AUTH_IDENTITY; diff --git a/Utilities/cmcurl/lib/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h index 2bbf9477bb0..c09026ebbb5 100644 --- a/Utilities/cmcurl/lib/curl_sspi.h +++ b/Utilities/cmcurl/lib/curl_sspi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c index 8e5937aa0bc..b5f10a20ec2 100644 --- a/Utilities/cmcurl/lib/curl_threads.c +++ b/Utilities/cmcurl/lib/curl_threads.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -48,7 +48,7 @@ struct curl_actual_call { static void *curl_thread_create_thunk(void *arg) { - struct curl_actual_call * ac = arg; + struct curl_actual_call *ac = arg; unsigned int (*func)(void *) = ac->func; void *real_arg = ac->arg; diff --git a/Utilities/cmcurl/lib/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h index 2a93644c56b..65d1a790c16 100644 --- a/Utilities/cmcurl/lib/curl_threads.h +++ b/Utilities/cmcurl/lib/curl_threads.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curlx.h b/Utilities/cmcurl/lib/curlx.h index 3e9b516f825..a8bae14b907 100644 --- a/Utilities/cmcurl/lib/curlx.h +++ b/Utilities/cmcurl/lib/curlx.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -53,6 +53,16 @@ curlx_uztosi() */ +#include "curl_multibyte.h" +/* "curl_multibyte.h" provides these functions and macros: + + curlx_convert_UTF8_to_wchar() + curlx_convert_wchar_to_UTF8() + curlx_convert_UTF8_to_tchar() + curlx_convert_tchar_to_UTF8() + curlx_unicodefree() +*/ + /* Now setup curlx_ * names for the functions that are to become curlx_ and be removed from a future libcurl official API: curlx_getenv diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c index 208a2336fde..f529b48f710 100644 --- a/Utilities/cmcurl/lib/dict.c +++ b/Utilities/cmcurl/lib/dict.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -46,6 +46,8 @@ #ifdef HAVE_SYS_SELECT_H #include +#elif defined(HAVE_UNISTD_H) +#include #endif #include "urldata.h" @@ -97,7 +99,8 @@ static char *unescape_word(struct Curl_easy *data, const char *inputbuff) char *dictp; size_t len; - CURLcode result = Curl_urldecode(data, inputbuff, 0, &newp, &len, FALSE); + CURLcode result = Curl_urldecode(data, inputbuff, 0, &newp, &len, + REJECT_NADA); if(!newp || result) return NULL; diff --git a/Utilities/cmcurl/lib/dict.h b/Utilities/cmcurl/lib/dict.h index 12c0f3394d5..38a55ac0dec 100644 --- a/Utilities/cmcurl/lib/dict.h +++ b/Utilities/cmcurl/lib/dict.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c index 6d1f3303b50..ebb2c243bc6 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2018 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,13 +35,13 @@ #include "curl_base64.h" #include "connect.h" #include "strdup.h" +#include "dynbuf.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" #define DNS_CLASS_IN 0x01 -#define DOH_MAX_RESPONSE_SIZE 3000 /* bytes */ #ifndef CURL_DISABLE_VERBOSE_STRINGS static const char * const errors[]={ @@ -74,17 +74,50 @@ static const char *doh_strerror(DOHcode code) #define UNITTEST static #endif +/* @unittest 1655 + */ UNITTEST DOHcode doh_encode(const char *host, DNStype dnstype, unsigned char *dnsp, /* buffer */ size_t len, /* buffer size */ size_t *olen) /* output length */ { - size_t hostlen = strlen(host); + const size_t hostlen = strlen(host); unsigned char *orig = dnsp; const char *hostp = host; - if(len < (12 + hostlen + 4)) + /* The expected output length is 16 bytes more than the length of + * the QNAME-encoding of the host name. + * + * A valid DNS name may not contain a zero-length label, except at + * the end. For this reason, a name beginning with a dot, or + * containing a sequence of two or more consecutive dots, is invalid + * and cannot be encoded as a QNAME. + * + * If the host name ends with a trailing dot, the corresponding + * QNAME-encoding is one byte longer than the host name. If (as is + * also valid) the hostname is shortened by the omission of the + * trailing dot, then its QNAME-encoding will be two bytes longer + * than the host name. + * + * Each [ label, dot ] pair is encoded as [ length, label ], + * preserving overall length. A final [ label ] without a dot is + * also encoded as [ length, label ], increasing overall length + * by one. The encoding is completed by appending a zero byte, + * representing the zero-length root label, again increasing + * the overall length by one. + */ + + size_t expected_len; + DEBUGASSERT(hostlen); + expected_len = 12 + 1 + hostlen + 4; + if(host[hostlen-1]!='.') + expected_len++; + + if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */ + return DOH_DNS_NAME_TOO_LONG; + + if(len < expected_len) return DOH_TOO_SMALL_BUFFER; *dnsp++ = 0; /* 16 bit id */ @@ -100,59 +133,55 @@ UNITTEST DOHcode doh_encode(const char *host, *dnsp++ = '\0'; *dnsp++ = '\0'; /* ARCOUNT */ - /* store a QNAME */ - do { - char *dot = strchr(hostp, '.'); + /* encode each label and store it in the QNAME */ + while(*hostp) { size_t labellen; - bool found = false; - if(dot) { - found = true; + char *dot = strchr(hostp, '.'); + if(dot) labellen = dot - hostp; - } else labellen = strlen(hostp); - if(labellen > 63) { - /* too long label, error out */ + if((labellen > 63) || (!labellen)) { + /* label is too long or too short, error out */ *olen = 0; return DOH_DNS_BAD_LABEL; } + /* label is non-empty, process it */ *dnsp++ = (unsigned char)labellen; memcpy(dnsp, hostp, labellen); dnsp += labellen; - hostp += labellen + 1; - if(!found) { - *dnsp++ = 0; /* terminating zero */ - break; - } - } while(1); + hostp += labellen; + /* advance past dot, but only if there is one */ + if(dot) + hostp++; + } /* next label */ + + *dnsp++ = 0; /* append zero-length label for root */ + + /* There are assigned TYPE codes beyond 255: use range [1..65535] */ + *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */ + *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */ - *dnsp++ = '\0'; /* upper 8 bit TYPE */ - *dnsp++ = (unsigned char)dnstype; *dnsp++ = '\0'; /* upper 8 bit CLASS */ *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */ *olen = dnsp - orig; + + /* verify that our estimation of length is valid, since + * this has led to buffer overflows in this function */ + DEBUGASSERT(*olen == expected_len); return DOH_OK; } static size_t -doh_write_cb(void *contents, size_t size, size_t nmemb, void *userp) +doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; - struct dohresponse *mem = (struct dohresponse *)userp; + struct dynbuf *mem = (struct dynbuf *)userp; - if((mem->size + realsize) > DOH_MAX_RESPONSE_SIZE) - /* suspiciously much for us */ + if(Curl_dyn_addn(mem, contents, realsize)) return 0; - mem->memory = Curl_saferealloc(mem->memory, mem->size + realsize); - if(!mem->memory) - /* out of memory! */ - return 0; - - memcpy(&(mem->memory[mem->size]), contents, realsize); - mem->size += realsize; - return realsize; } @@ -180,7 +209,7 @@ do { \ result = curl_easy_setopt(doh, x, y); \ if(result) \ goto error; \ -} WHILE_FALSE +} while(0) static CURLcode dohprobe(struct Curl_easy *data, struct dnsprobe *p, DNStype dnstype, @@ -200,10 +229,7 @@ static CURLcode dohprobe(struct Curl_easy *data, } p->dnstype = dnstype; - p->serverdoh.memory = NULL; - /* the memory will be grown as needed by realloc in the doh_write_cb - function */ - p->serverdoh.size = 0; + Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE); /* Note: this is code for sending the DoH request with GET but there's still no logic that actually enables this. We should either add that ability or @@ -225,13 +251,16 @@ static CURLcode dohprobe(struct Curl_easy *data, } timeout_ms = Curl_timeleft(data, NULL, TRUE); - + if(timeout_ms <= 0) { + result = CURLE_OPERATION_TIMEDOUT; + goto error; + } /* Curl_open() is the internal version of curl_easy_init() */ result = Curl_open(&doh); if(!result) { /* pass in the struct pointer via a local variable to please coverity and the gcc typecheck helpers */ - struct dohresponse *resp = &p->serverdoh; + struct dynbuf *resp = &p->serverdoh; ERROR_CHECK_SETOPT(CURLOPT_URL, url); ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp); @@ -246,6 +275,9 @@ static CURLcode dohprobe(struct Curl_easy *data, #ifndef CURLDEBUG /* enforce HTTPS if not debug */ ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); +#else + /* in debug mode, also allow http */ + ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); #endif ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); if(data->set.verbose) @@ -259,38 +291,45 @@ static CURLcode dohprobe(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L); if(data->set.ssl.primary.verifyhost) ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L); +#ifndef CURL_DISABLE_PROXY if(data->set.proxy_ssl.primary.verifyhost) ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L); - if(data->set.ssl.primary.verifypeer) - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L); if(data->set.proxy_ssl.primary.verifypeer) ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L); + if(data->set.str[STRING_SSL_CAFILE_PROXY]) { + ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO, + data->set.str[STRING_SSL_CAFILE_PROXY]); + } + if(data->set.str[STRING_SSL_CRLFILE_PROXY]) { + ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE, + data->set.str[STRING_SSL_CRLFILE_PROXY]); + } + if(data->set.proxy_ssl.no_revoke) + ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); + else if(data->set.proxy_ssl.revoke_best_effort) + ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, + CURLSSLOPT_REVOKE_BEST_EFFORT); + if(data->set.str[STRING_SSL_CAPATH_PROXY]) { + ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH, + data->set.str[STRING_SSL_CAPATH_PROXY]); + } +#endif + if(data->set.ssl.primary.verifypeer) + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L); if(data->set.ssl.primary.verifystatus) ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L); if(data->set.str[STRING_SSL_CAFILE_ORIG]) { ERROR_CHECK_SETOPT(CURLOPT_CAINFO, data->set.str[STRING_SSL_CAFILE_ORIG]); } - if(data->set.str[STRING_SSL_CAFILE_PROXY]) { - ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO, - data->set.str[STRING_SSL_CAFILE_PROXY]); - } if(data->set.str[STRING_SSL_CAPATH_ORIG]) { ERROR_CHECK_SETOPT(CURLOPT_CAPATH, data->set.str[STRING_SSL_CAPATH_ORIG]); } - if(data->set.str[STRING_SSL_CAPATH_PROXY]) { - ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH, - data->set.str[STRING_SSL_CAPATH_PROXY]); - } if(data->set.str[STRING_SSL_CRLFILE_ORIG]) { ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, data->set.str[STRING_SSL_CRLFILE_ORIG]); } - if(data->set.str[STRING_SSL_CRLFILE_PROXY]) { - ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE, - data->set.str[STRING_SSL_CRLFILE_PROXY]); - } if(data->set.ssl.certinfo) ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); if(data->set.str[STRING_SSL_RANDOM_FILE]) { @@ -303,8 +342,8 @@ static CURLcode dohprobe(struct Curl_easy *data, } if(data->set.ssl.no_revoke) ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); - if(data->set.proxy_ssl.no_revoke) - ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); + else if(data->set.ssl.revoke_best_effort) + ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT); if(data->set.ssl.fsslctx) ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); if(data->set.ssl.fsslctxp) @@ -325,7 +364,7 @@ static CURLcode dohprobe(struct Curl_easy *data, error: free(nurl); - Curl_close(doh); + Curl_close(&doh); return result; } @@ -334,13 +373,14 @@ static CURLcode dohprobe(struct Curl_easy *data, * 'Curl_addrinfo *' with the address information. */ -Curl_addrinfo *Curl_doh(struct connectdata *conn, - const char *hostname, - int port, - int *waitp) +struct Curl_addrinfo *Curl_doh(struct connectdata *conn, + const char *hostname, + int port, + int *waitp) { struct Curl_easy *data = conn->data; CURLcode result = CURLE_OK; + int slot; *waitp = TRUE; /* this never returns synchronously */ (void)conn; (void)hostname; @@ -349,6 +389,7 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn, /* start clean, consider allocating this struct on demand */ memset(&data->req.doh, 0, sizeof(struct dohdata)); + conn->bits.doh = TRUE; data->req.doh.host = hostname; data->req.doh.port = port; data->req.doh.headers = @@ -359,8 +400,8 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn, if(conn->ip_version != CURL_IPRESOLVE_V6) { /* create IPv4 DOH request */ - result = dohprobe(data, &data->req.doh.probe[0], DNS_TYPE_A, - hostname, data->set.str[STRING_DOH], + result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4], + DNS_TYPE_A, hostname, data->set.str[STRING_DOH], data->multi, data->req.doh.headers); if(result) goto error; @@ -369,8 +410,8 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn, if(conn->ip_version != CURL_IPRESOLVE_V4) { /* create IPv6 DOH request */ - result = dohprobe(data, &data->req.doh.probe[1], DNS_TYPE_AAAA, - hostname, data->set.str[STRING_DOH], + result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6], + DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH], data->multi, data->req.doh.headers); if(result) goto error; @@ -381,14 +422,13 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn, error: curl_slist_free_all(data->req.doh.headers); data->req.doh.headers = NULL; - curl_easy_cleanup(data->req.doh.probe[0].easy); - data->req.doh.probe[0].easy = NULL; - curl_easy_cleanup(data->req.doh.probe[1].easy); - data->req.doh.probe[1].easy = NULL; + for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { + Curl_close(&data->req.doh.probe[slot].easy); + } return NULL; } -static DOHcode skipqname(unsigned char *doh, size_t dohlen, +static DOHcode skipqname(const unsigned char *doh, size_t dohlen, unsigned int *indexp) { unsigned char length; @@ -412,18 +452,24 @@ static DOHcode skipqname(unsigned char *doh, size_t dohlen, return DOH_OK; } -static unsigned short get16bit(unsigned char *doh, int index) +static unsigned short get16bit(const unsigned char *doh, int index) { return (unsigned short)((doh[index] << 8) | doh[index + 1]); } -static unsigned int get32bit(unsigned char *doh, int index) +static unsigned int get32bit(const unsigned char *doh, int index) { - return (doh[index] << 24) | (doh[index + 1] << 16) | - (doh[index + 2] << 8) | doh[index + 3]; + /* make clang and gcc optimize this to bswap by incrementing + the pointer first. */ + doh += index; + + /* avoid undefined behaviour by casting to unsigned before shifting + 24 bits, possibly into the sign bit. codegen is same, but + ub sanitizer won't be upset */ + return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3]; } -static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d) +static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d) { /* silently ignore addresses over the limit */ if(d->numaddr < DOH_MAX_ADDR) { @@ -435,7 +481,9 @@ static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d) return DOH_OK; } -static DOHcode store_aaaa(unsigned char *doh, int index, struct dohentry *d) +static DOHcode store_aaaa(const unsigned char *doh, + int index, + struct dohentry *d) { /* silently ignore addresses over the limit */ if(d->numaddr < DOH_MAX_ADDR) { @@ -447,38 +495,12 @@ static DOHcode store_aaaa(unsigned char *doh, int index, struct dohentry *d) return DOH_OK; } -static DOHcode cnameappend(struct cnamestore *c, - unsigned char *src, - size_t len) -{ - if(!c->alloc) { - c->allocsize = len + 1; - c->alloc = malloc(c->allocsize); - if(!c->alloc) - return DOH_OUT_OF_MEM; - } - else if(c->allocsize < (c->allocsize + len + 1)) { - char *ptr; - c->allocsize += len + 1; - ptr = realloc(c->alloc, c->allocsize); - if(!ptr) { - free(c->alloc); - return DOH_OUT_OF_MEM; - } - c->alloc = ptr; - } - memcpy(&c->alloc[c->len], src, len); - c->len += len; - c->alloc[c->len] = 0; /* keep it zero terminated */ - return DOH_OK; -} - -static DOHcode store_cname(unsigned char *doh, +static DOHcode store_cname(const unsigned char *doh, size_t dohlen, unsigned int index, struct dohentry *d) { - struct cnamestore *c; + struct dynbuf *c; unsigned int loop = 128; /* a valid DNS name can never loop this much */ unsigned char length; @@ -496,7 +518,7 @@ static DOHcode store_cname(unsigned char *doh, if((index + 1) >= dohlen) return DOH_DNS_OUT_OF_RANGE; - /* move to the the new index */ + /* move to the new index */ newpos = (length & 0x3f) << 8 | doh[index + 1]; index = newpos; continue; @@ -507,18 +529,15 @@ static DOHcode store_cname(unsigned char *doh, index++; if(length) { - DOHcode rc; - if(c->len) { - rc = cnameappend(c, (unsigned char *)".", 1); - if(rc) - return rc; + if(Curl_dyn_len(c)) { + if(Curl_dyn_add(c, ".")) + return DOH_OUT_OF_MEM; } if((index + length) > dohlen) return DOH_DNS_BAD_LABEL; - rc = cnameappend(c, &doh[index], length); - if(rc) - return rc; + if(Curl_dyn_addn(c, &doh[index], length)) + return DOH_OUT_OF_MEM; index += length; } } while(length && --loop); @@ -528,7 +547,7 @@ static DOHcode store_cname(unsigned char *doh, return DOH_OK; } -static DOHcode rdata(unsigned char *doh, +static DOHcode rdata(const unsigned char *doh, size_t dohlen, unsigned short rdlength, unsigned short type, @@ -561,6 +580,9 @@ static DOHcode rdata(unsigned char *doh, if(rc) return rc; break; + case DNS_TYPE_DNAME: + /* explicit for clarity; just skip; rely on synthesized CNAME */ + break; default: /* unsupported type, just skip it */ break; @@ -568,14 +590,17 @@ static DOHcode rdata(unsigned char *doh, return DOH_OK; } -static void init_dohentry(struct dohentry *de) +UNITTEST void de_init(struct dohentry *de) { + int i; memset(de, 0, sizeof(*de)); de->ttl = INT_MAX; + for(i = 0; i < DOH_MAX_CNAME; i++) + Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME); } -UNITTEST DOHcode doh_decode(unsigned char *doh, +UNITTEST DOHcode doh_decode(const unsigned char *doh, size_t dohlen, DNStype dnstype, struct dohentry *d) @@ -622,8 +647,10 @@ UNITTEST DOHcode doh_decode(unsigned char *doh, return DOH_DNS_OUT_OF_RANGE; type = get16bit(doh, index); - if((type != DNS_TYPE_CNAME) && (type != dnstype)) - /* Not the same type as was asked for nor CNAME */ + if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */ + && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */ + && (type != dnstype)) + /* Not the same type as was asked for nor CNAME nor DNAME */ return DOH_DNS_UNEXPECTED_TYPE; index += 2; @@ -713,12 +740,12 @@ UNITTEST DOHcode doh_decode(unsigned char *doh, #ifndef CURL_DISABLE_VERBOSE_STRINGS static void showdoh(struct Curl_easy *data, - struct dohentry *d) + const struct dohentry *d) { int i; infof(data, "TTL: %u seconds\n", d->ttl); for(i = 0; i < d->numaddr; i++) { - struct dohaddr *a = &d->addr[i]; + const struct dohaddr *a = &d->addr[i]; if(a->type == DNS_TYPE_A) { infof(data, "DOH A: %u.%u.%u.%u\n", a->ip.v4[0], a->ip.v4[1], @@ -744,7 +771,7 @@ static void showdoh(struct Curl_easy *data, } } for(i = 0; i < d->numcname; i++) { - infof(data, "CNAME: %s\n", d->cname[i].alloc); + infof(data, "CNAME: %s\n", Curl_dyn_ptr(&d->cname[i])); } } #else @@ -764,18 +791,19 @@ static void showdoh(struct Curl_easy *data, * must be an associated call later to Curl_freeaddrinfo(). */ -static Curl_addrinfo * +static struct Curl_addrinfo * doh2ai(const struct dohentry *de, const char *hostname, int port) { - Curl_addrinfo *ai; - Curl_addrinfo *prevai = NULL; - Curl_addrinfo *firstai = NULL; + struct Curl_addrinfo *ai; + struct Curl_addrinfo *prevai = NULL; + struct Curl_addrinfo *firstai = NULL; struct sockaddr_in *addr; #ifdef ENABLE_IPV6 struct sockaddr_in6 *addr6; #endif CURLcode result = CURLE_OK; int i; + size_t hostlen = strlen(hostname) + 1; /* include zero terminator */ if(!de) /* no input == no output! */ @@ -798,24 +826,14 @@ doh2ai(const struct dohentry *de, const char *hostname, int port) addrtype = AF_INET; } - ai = calloc(1, sizeof(Curl_addrinfo)); + ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen); if(!ai) { result = CURLE_OUT_OF_MEMORY; break; } - ai->ai_canonname = strdup(hostname); - if(!ai->ai_canonname) { - result = CURLE_OUT_OF_MEMORY; - free(ai); - break; - } - ai->ai_addr = calloc(1, ss_size); - if(!ai->ai_addr) { - result = CURLE_OUT_OF_MEMORY; - free(ai->ai_canonname); - free(ai); - break; - } + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); + ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size); + memcpy(ai->ai_canonname, hostname, hostlen); if(!firstai) /* store the pointer we want to return from this function */ @@ -877,54 +895,54 @@ UNITTEST void de_cleanup(struct dohentry *d) { int i = 0; for(i = 0; i < d->numcname; i++) { - free(d->cname[i].alloc); + Curl_dyn_free(&d->cname[i]); } } CURLcode Curl_doh_is_resolved(struct connectdata *conn, struct Curl_dns_entry **dnsp) { + CURLcode result; struct Curl_easy *data = conn->data; *dnsp = NULL; /* defaults to no response */ - if(!data->req.doh.probe[0].easy && !data->req.doh.probe[1].easy) { + if(!data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4].easy && + !data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6].easy) { failf(data, "Could not DOH-resolve: %s", conn->async.hostname); return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } else if(!data->req.doh.pending) { - DOHcode rc; - DOHcode rc2; + DOHcode rc[DOH_PROBE_SLOTS] = { + DOH_OK, DOH_OK + }; struct dohentry de; + int slot; /* remove DOH handles from multi handle and close them */ - curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy); - Curl_close(data->req.doh.probe[0].easy); - curl_multi_remove_handle(data->multi, data->req.doh.probe[1].easy); - Curl_close(data->req.doh.probe[1].easy); - - /* parse the responses, create the struct and return it! */ - init_dohentry(&de); - rc = doh_decode(data->req.doh.probe[0].serverdoh.memory, - data->req.doh.probe[0].serverdoh.size, - data->req.doh.probe[0].dnstype, - &de); - free(data->req.doh.probe[0].serverdoh.memory); - if(rc) { - infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc), - type2name(data->req.doh.probe[0].dnstype), - data->req.doh.host); + for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { + curl_multi_remove_handle(data->multi, data->req.doh.probe[slot].easy); + Curl_close(&data->req.doh.probe[slot].easy); } - rc2 = doh_decode(data->req.doh.probe[1].serverdoh.memory, - data->req.doh.probe[1].serverdoh.size, - data->req.doh.probe[1].dnstype, - &de); - free(data->req.doh.probe[1].serverdoh.memory); - if(rc2) { - infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc2), - type2name(data->req.doh.probe[1].dnstype), - data->req.doh.host); - } - if(!rc || !rc2) { + /* parse the responses, create the struct and return it! */ + de_init(&de); + for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { + struct dnsprobe *p = &data->req.doh.probe[slot]; + if(!p->dnstype) + continue; + rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh), + Curl_dyn_len(&p->serverdoh), + p->dnstype, + &de); + Curl_dyn_free(&p->serverdoh); + if(rc[slot]) { + infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc[slot]), + type2name(p->dnstype), data->req.doh.host); + } + } /* next slot */ + + result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ + if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) { + /* we have an address, of one kind or other */ struct Curl_dns_entry *dns; struct Curl_addrinfo *ai; @@ -946,21 +964,26 @@ CURLcode Curl_doh_is_resolved(struct connectdata *conn, if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); - de_cleanup(&de); - if(!dns) + if(!dns) { /* returned failure, bail out nicely */ Curl_freeaddrinfo(ai); + } else { conn->async.dns = dns; *dnsp = dns; - return CURLE_OK; + result = CURLE_OK; /* address resolution OK */ } - } + } /* address processing done */ + + /* Now process any build-specific attributes retrieved from DNS */ + + /* All done */ de_cleanup(&de); + return result; - return CURLE_COULDNT_RESOLVE_HOST; - } + } /* !data->req.doh.pending */ + /* else wait for pending DOH transactions to complete */ return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h index 34bfa6f2ba6..bbd4c1a65ee 100644 --- a/Utilities/cmcurl/lib/doh.h +++ b/Utilities/cmcurl/lib/doh.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2018 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,16 +32,15 @@ * and returns a 'Curl_addrinfo *' with the address information. */ -Curl_addrinfo *Curl_doh(struct connectdata *conn, - const char *hostname, - int port, - int *waitp); +struct Curl_addrinfo *Curl_doh(struct connectdata *conn, + const char *hostname, + int port, + int *waitp); CURLcode Curl_doh_is_resolved(struct connectdata *conn, struct Curl_dns_entry **dns); -int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); +int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks); typedef enum { DOH_OK, @@ -56,25 +55,21 @@ typedef enum { DOH_DNS_UNEXPECTED_TYPE, /* 9 */ DOH_DNS_UNEXPECTED_CLASS, /* 10 */ DOH_NO_CONTENT, /* 11 */ - DOH_DNS_BAD_ID /* 12 */ + DOH_DNS_BAD_ID, /* 12 */ + DOH_DNS_NAME_TOO_LONG /* 13 */ } DOHcode; typedef enum { DNS_TYPE_A = 1, DNS_TYPE_NS = 2, DNS_TYPE_CNAME = 5, - DNS_TYPE_AAAA = 28 + DNS_TYPE_AAAA = 28, + DNS_TYPE_DNAME = 39 /* RFC6672 */ } DNStype; #define DOH_MAX_ADDR 24 #define DOH_MAX_CNAME 4 -struct cnamestore { - size_t len; /* length of cname */ - char *alloc; /* allocated pointer */ - size_t allocsize; /* allocated size */ -}; - struct dohaddr { int type; union { @@ -84,11 +79,11 @@ struct dohaddr { }; struct dohentry { - unsigned int ttl; - int numaddr; + struct dynbuf cname[DOH_MAX_CNAME]; struct dohaddr addr[DOH_MAX_ADDR]; + int numaddr; + unsigned int ttl; int numcname; - struct cnamestore cname[DOH_MAX_CNAME]; }; @@ -98,10 +93,11 @@ DOHcode doh_encode(const char *host, unsigned char *dnsp, /* buffer */ size_t len, /* buffer size */ size_t *olen); /* output length */ -DOHcode doh_decode(unsigned char *doh, +DOHcode doh_decode(const unsigned char *doh, size_t dohlen, DNStype dnstype, struct dohentry *d); +void de_init(struct dohentry *d); void de_cleanup(struct dohentry *d); #endif diff --git a/Utilities/cmcurl/lib/dotdot.c b/Utilities/cmcurl/lib/dotdot.c index 2c6177aeada..ce9a052143c 100644 --- a/Utilities/cmcurl/lib/dotdot.c +++ b/Utilities/cmcurl/lib/dotdot.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -39,7 +39,7 @@ * Curl_dedotdotify() * @unittest: 1395 * - * This function gets a zero-terminated path with dot and dotdot sequences + * 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 * 5.2.4. * @@ -62,7 +62,7 @@ char *Curl_dedotdotify(const char *input) if(!out) return NULL; /* out of memory */ - *out = 0; /* zero terminates, for inputs like "./" */ + *out = 0; /* null-terminates, for inputs like "./" */ /* get a cloned copy of the input */ clone = strdup(input); @@ -129,7 +129,7 @@ char *Curl_dedotdotify(const char *input) if(*outptr == '/') break; } - *outptr = 0; /* zero-terminate where it stops */ + *outptr = 0; /* null-terminate where it stops */ } else if(!strcmp("/..", clone)) { clone[2]='/'; @@ -141,7 +141,7 @@ char *Curl_dedotdotify(const char *input) if(*outptr == '/') break; } - *outptr = 0; /* zero-terminate where it stops */ + *outptr = 0; /* null-terminate where it stops */ } /* D. if the input buffer consists only of "." or "..", then remove diff --git a/Utilities/cmcurl/lib/dotdot.h b/Utilities/cmcurl/lib/dotdot.h index 125af436717..f70b1db3fe7 100644 --- a/Utilities/cmcurl/lib/dotdot.h +++ b/Utilities/cmcurl/lib/dotdot.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c new file mode 100644 index 00000000000..38d370b078d --- /dev/null +++ b/Utilities/cmcurl/lib/dynbuf.c @@ -0,0 +1,227 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "strdup.h" +#include "dynbuf.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define MIN_FIRST_ALLOC 32 + +#define DYNINIT 0xbee51da /* random pattern */ + +/* + * Init a dynbuf struct. + */ +void Curl_dyn_init(struct dynbuf *s, size_t toobig) +{ + DEBUGASSERT(s); + DEBUGASSERT(toobig); + s->bufr = NULL; + s->leng = 0; + s->allc = 0; + s->toobig = toobig; +#ifdef DEBUGBUILD + s->init = DYNINIT; +#endif +} + +/* + * free the buffer and re-init the necessary fields. It doesn't touch the + * 'init' field and thus this buffer can be reused to add data to again. + */ +void Curl_dyn_free(struct dynbuf *s) +{ + DEBUGASSERT(s); + Curl_safefree(s->bufr); + s->leng = s->allc = 0; +} + +/* + * Store/append an chunk of memory to the dynbuf. + */ +static CURLcode dyn_nappend(struct dynbuf *s, + const unsigned char *mem, size_t len) +{ + size_t indx = s->leng; + size_t a = s->allc; + size_t fit = len + indx + 1; /* new string + old string + zero byte */ + + /* try to detect if there's rubbish in the struct */ + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(s->toobig); + DEBUGASSERT(indx < s->toobig); + DEBUGASSERT(!s->leng || s->bufr); + + if(fit > s->toobig) { + Curl_dyn_free(s); + return CURLE_OUT_OF_MEMORY; + } + else if(!a) { + DEBUGASSERT(!indx); + /* first invoke */ + if(fit < MIN_FIRST_ALLOC) + a = MIN_FIRST_ALLOC; + else + a = fit; + } + else { + while(a < fit) + a *= 2; + } + + if(a != s->allc) { + s->bufr = Curl_saferealloc(s->bufr, a); + if(!s->bufr) { + s->leng = s->allc = 0; + return CURLE_OUT_OF_MEMORY; + } + s->allc = a; + } + + if(len) + memcpy(&s->bufr[indx], mem, len); + s->leng = indx + len; + s->bufr[s->leng] = 0; + return CURLE_OK; +} + +/* + * Clears the string, keeps the allocation. This can also be called on a + * buffer that already was freed. + */ +void Curl_dyn_reset(struct dynbuf *s) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + if(s->leng) + s->bufr[0] = 0; + s->leng = 0; +} + +#ifdef USE_NGTCP2 +/* + * Specify the size of the tail to keep (number of bytes from the end of the + * buffer). The rest will be dropped. + */ +CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + if(trail > s->leng) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(trail == s->leng) + return CURLE_OK; + else if(!trail) { + Curl_dyn_reset(s); + } + else { + memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail); + s->leng = trail; + } + return CURLE_OK; + +} +#endif + +/* + * Appends a buffer with length. + */ +CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + return dyn_nappend(s, mem, len); +} + +/* + * Append a null-terminated string at the end. + */ +CURLcode Curl_dyn_add(struct dynbuf *s, const char *str) +{ + size_t n = strlen(str); + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + return dyn_nappend(s, (unsigned char *)str, n); +} + +/* + * Append a string printf()-style + */ +CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...) +{ + char *str; + va_list ap; + va_start(ap, fmt); + str = vaprintf(fmt, ap); /* this allocs a new string to append */ + va_end(ap); + + if(str) { + CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str)); + free(str); + return result; + } + /* If we failed, we cleanup the whole buffer and return error */ + Curl_dyn_free(s); + return CURLE_OUT_OF_MEMORY; +} + +/* + * Returns a pointer to the buffer. + */ +char *Curl_dyn_ptr(const struct dynbuf *s) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + return s->bufr; +} + +/* + * Returns an unsigned pointer to the buffer. + */ +unsigned char *Curl_dyn_uptr(const struct dynbuf *s) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + return (unsigned char *)s->bufr; +} + +/* + * Returns the length of the buffer. + */ +size_t Curl_dyn_len(const struct dynbuf *s) +{ + DEBUGASSERT(s); + DEBUGASSERT(s->init == DYNINIT); + DEBUGASSERT(!s->leng || s->bufr); + return s->leng; +} diff --git a/Utilities/cmcurl/lib/dynbuf.h b/Utilities/cmcurl/lib/dynbuf.h new file mode 100644 index 00000000000..c80239e29c7 --- /dev/null +++ b/Utilities/cmcurl/lib/dynbuf.h @@ -0,0 +1,63 @@ +#ifndef HEADER_CURL_DYNBUF_H +#define HEADER_CURL_DYNBUF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +struct dynbuf { + char *bufr; /* point to a null-terminated allocated buffer */ + size_t leng; /* number of bytes *EXCLUDING* the zero terminator */ + size_t allc; /* size of the current allocation */ + size_t toobig; /* size limit for the buffer */ +#ifdef DEBUGBUILD + int init; /* detect API usage mistakes */ +#endif +}; + +void Curl_dyn_init(struct dynbuf *s, size_t toobig); +void Curl_dyn_free(struct dynbuf *s); +CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len) + WARN_UNUSED_RESULT; +CURLcode Curl_dyn_add(struct dynbuf *s, const char *str) + WARN_UNUSED_RESULT; +CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...) + WARN_UNUSED_RESULT; +void Curl_dyn_reset(struct dynbuf *s); +CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail); +char *Curl_dyn_ptr(const struct dynbuf *s); +unsigned char *Curl_dyn_uptr(const struct dynbuf *s); +size_t Curl_dyn_len(const struct dynbuf *s); + +/* Dynamic buffer max sizes */ +#define DYN_DOH_RESPONSE 3000 +#define DYN_DOH_CNAME 256 +#define DYN_PAUSE_BUFFER (64 * 1024 * 1024) +#define DYN_HAXPROXY 2048 +#define DYN_HTTP_REQUEST (128*1024) +#define DYN_H2_HEADERS (128*1024) +#define DYN_H2_TRAILER 4096 +#define DYN_APRINTF 8000000 +#define DYN_RTSP_REQ_HEADER (64*1024) +#define DYN_TRAILERS (64*1024) +#define DYN_PROXY_CONNECT_HEADERS 16384 +#define DYN_QLOG_NAME 1024 +#define DYN_H1_TRAILER DYN_H2_TRAILER +#endif diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index 4a6f9656776..292cca7f6f0 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -72,18 +72,18 @@ #include "warnless.h" #include "multiif.h" #include "sigpipe.h" -#include "ssh.h" +#include "vssh/ssh.h" #include "setopt.h" #include "http_digest.h" #include "system_win32.h" +#include "http2.h" +#include "dynbuf.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -void Curl_version_init(void); - /* true globals -- for curl_global_init() and curl_global_cleanup() */ static unsigned int initialized; static long init_flags; @@ -157,20 +157,20 @@ static CURLcode global_init(long flags, bool memoryfuncs) if(!Curl_ssl_init()) { DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); - return CURLE_FAILED_INIT; + goto fail; } #ifdef WIN32 if(Curl_win32_init(flags)) { DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); - return CURLE_FAILED_INIT; + goto fail; } #endif #ifdef __AMIGA__ if(!Curl_amiga_init()) { DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); - return CURLE_FAILED_INIT; + goto fail; } #endif @@ -182,33 +182,29 @@ static CURLcode global_init(long flags, bool memoryfuncs) if(Curl_resolver_global_init()) { DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); - return CURLE_FAILED_INIT; + goto fail; } - (void)Curl_ipv6works(); - -#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_INIT) - if(libssh2_init(0)) { - DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n")); - return CURLE_FAILED_INIT; +#if defined(USE_SSH) + if(Curl_ssh_init()) { + goto fail; } #endif -#if defined(USE_LIBSSH) - if(ssh_init()) { - DEBUGF(fprintf(stderr, "Error: libssh_init failed\n")); +#ifdef USE_WOLFSSH + if(WS_SUCCESS != wolfSSH_Init()) { + DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n")); return CURLE_FAILED_INIT; } #endif - if(flags & CURL_GLOBAL_ACK_EINTR) - Curl_ack_eintr = 1; - init_flags = flags; - Curl_version_init(); - return CURLE_OK; + + fail: + initialized--; /* undo the increase */ + return CURLE_FAILED_INIT; } @@ -274,12 +270,10 @@ void curl_global_cleanup(void) Curl_amiga_cleanup(); -#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_EXIT) - (void)libssh2_exit(); -#endif + Curl_ssh_cleanup(); -#if defined(USE_LIBSSH) - (void)ssh_finalize(); +#ifdef USE_WOLFSSH + (void)wolfSSH_Cleanup(); #endif init_flags = 0; @@ -516,7 +510,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) before = Curl_now(); /* wait for activity or timeout */ - pollrc = Curl_poll(fds, numfds, (int)ev->ms); + pollrc = Curl_poll(fds, numfds, ev->ms); after = Curl_now(); @@ -602,27 +596,11 @@ static CURLcode easy_transfer(struct Curl_multi *multi) while(!done && !mcode) { int still_running = 0; - bool gotsocket = FALSE; - - mcode = Curl_multi_wait(multi, NULL, 0, 1000, NULL, &gotsocket); - - if(!mcode) { - if(!gotsocket) { - long sleep_ms; - - /* If it returns without any filedescriptor instantly, we need to - avoid busy-looping during periods where it has nothing particular - to wait for */ - curl_multi_timeout(multi, &sleep_ms); - if(sleep_ms) { - if(sleep_ms > 1000) - sleep_ms = 1000; - Curl_wait_ms((int)sleep_ms); - } - } + mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL); + + if(!mcode) mcode = curl_multi_perform(multi, &still_running); - } /* only read 'still_running' if curl_multi_perform() return OK */ if(!mcode && !still_running) { @@ -703,6 +681,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) mcode = curl_multi_add_handle(multi, data); if(mcode) { curl_multi_cleanup(multi); + data->multi_easy = NULL; if(mcode == CURLM_OUT_OF_MEMORY) return CURLE_OUT_OF_MEMORY; return CURLE_FAILED_INIT; @@ -710,10 +689,6 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) sigpipe_ignore(data, &pipe_st); - /* assign this after curl_multi_add_handle() since that function checks for - it and rejects this handle otherwise */ - data->multi = multi; - /* run the transfer */ result = events ? easy_events(multi) : easy_transfer(multi); @@ -761,7 +736,7 @@ void curl_easy_cleanup(struct Curl_easy *data) return; sigpipe_ignore(data, &pipe_st); - Curl_close(data); + Curl_close(&data); sigpipe_restore(&pipe_st); } @@ -789,6 +764,7 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) { CURLcode result = CURLE_OK; enum dupstring i; + enum dupblob j; /* Copy src->set into dst->set first, then deal with the strings afterwards */ @@ -805,6 +781,16 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) return result; } + /* clear all blob pointers first */ + memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); + /* duplicate all blobs */ + for(j = (enum dupblob)0; j < BLOB_LAST; j++) { + result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]); + /* Curl_setstropt return CURLE_BAD_FUNCTION_ARGUMENT with blob */ + if(result) + return result; + } + /* duplicate memory areas pointed to */ i = STRING_COPYPOSTFIELDS; if(src->set.postfieldsize && src->set.str[i]) { @@ -843,19 +829,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) * the likeliness of us forgetting to init a buffer here in the future. */ outcurl->set.buffer_size = data->set.buffer_size; - outcurl->state.buffer = malloc(outcurl->set.buffer_size + 1); - if(!outcurl->state.buffer) - goto fail; - - outcurl->state.headerbuff = malloc(HEADERSIZE); - if(!outcurl->state.headerbuff) - goto fail; - outcurl->state.headersize = HEADERSIZE; /* copy all userdefined values */ if(dupset(outcurl, data)) goto fail; + Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); + /* the connection cache is setup on demand */ outcurl->state.conn_cache = NULL; @@ -910,6 +890,28 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) data->state.resolver)) goto fail; +#ifdef USE_ARES + { + CURLcode rc; + + rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]); + if(rc && rc != CURLE_NOT_BUILT_IN) + goto fail; + + rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]); + if(rc && rc != CURLE_NOT_BUILT_IN) + goto fail; + + rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]); + if(rc && rc != CURLE_NOT_BUILT_IN) + goto fail; + + rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]); + if(rc && rc != CURLE_NOT_BUILT_IN) + goto fail; + } +#endif /* USE_ARES */ + Curl_convert_setup(outcurl); Curl_initinfo(outcurl); @@ -926,7 +928,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) curl_slist_free_all(outcurl->change.cookielist); outcurl->change.cookielist = NULL; Curl_safefree(outcurl->state.buffer); - Curl_safefree(outcurl->state.headerbuff); + Curl_dyn_free(&outcurl->state.headerb); Curl_safefree(outcurl->change.url); Curl_safefree(outcurl->change.referer); Curl_freeset(outcurl); @@ -982,68 +984,101 @@ void curl_easy_reset(struct Curl_easy *data) */ CURLcode curl_easy_pause(struct Curl_easy *data, int action) { - struct SingleRequest *k = &data->req; + struct SingleRequest *k; CURLcode result = CURLE_OK; + int oldstate; + int newstate; - /* first switch off both pause bits */ - int newstate = k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE); + if(!GOOD_EASY_HANDLE(data) || !data->conn) + /* crazy input, don't continue */ + return CURLE_BAD_FUNCTION_ARGUMENT; - /* set the new desired pause bits */ - newstate |= ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) | + k = &data->req; + oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE); + + /* first switch off both pause bits then set the new pause bits */ + newstate = (k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) | + ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) | ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0); + if((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) == oldstate) { + /* Not changing any pause state, return */ + DEBUGF(infof(data, "pause: no change, early return\n")); + return CURLE_OK; + } + + /* Unpause parts in active mime tree. */ + if((k->keepon & ~newstate & KEEP_SEND_PAUSE) && + (data->mstate == CURLM_STATE_PERFORM || + data->mstate == CURLM_STATE_TOOFAST) && + data->state.fread_func == (curl_read_callback) Curl_mime_read) { + Curl_mime_unpause(data->state.in); + } + /* put it back in the keepon */ k->keepon = newstate; - if(!(newstate & KEEP_RECV_PAUSE) && data->state.tempcount) { - /* there are buffers for sending that can be delivered as the receive - pausing is lifted! */ - unsigned int i; - unsigned int count = data->state.tempcount; - struct tempbuf writebuf[3]; /* there can only be three */ - struct connectdata *conn = data->conn; - struct Curl_easy *saved_data = NULL; - - /* copy the structs to allow for immediate re-pausing */ - for(i = 0; i < data->state.tempcount; i++) { - writebuf[i] = data->state.tempwrite[i]; - data->state.tempwrite[i].buf = NULL; - } - data->state.tempcount = 0; + if(!(newstate & KEEP_RECV_PAUSE)) { + Curl_http2_stream_pause(data, FALSE); + + if(data->state.tempcount) { + /* there are buffers for sending that can be delivered as the receive + pausing is lifted! */ + unsigned int i; + unsigned int count = data->state.tempcount; + struct tempbuf writebuf[3]; /* there can only be three */ + struct connectdata *conn = data->conn; + struct Curl_easy *saved_data = NULL; + + /* copy the structs to allow for immediate re-pausing */ + for(i = 0; i < data->state.tempcount; i++) { + writebuf[i] = data->state.tempwrite[i]; + Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER); + } + data->state.tempcount = 0; - /* set the connection's current owner */ - if(conn->data != data) { - saved_data = conn->data; - conn->data = data; - } + /* set the connection's current owner */ + if(conn->data != data) { + saved_data = conn->data; + conn->data = data; + } - for(i = 0; i < count; i++) { - /* even if one function returns error, this loops through and frees all - buffers */ - if(!result) - result = Curl_client_write(conn, writebuf[i].type, writebuf[i].buf, - writebuf[i].len); - free(writebuf[i].buf); - } + for(i = 0; i < count; i++) { + /* even if one function returns error, this loops through and frees + all buffers */ + if(!result) + result = Curl_client_write(conn, writebuf[i].type, + Curl_dyn_ptr(&writebuf[i].b), + Curl_dyn_len(&writebuf[i].b)); + Curl_dyn_free(&writebuf[i].b); + } - /* recover previous owner of the connection */ - if(saved_data) - conn->data = saved_data; + /* recover previous owner of the connection */ + if(saved_data) + conn->data = saved_data; - if(result) - return result; + if(result) + return result; + } } /* if there's no error and we're not pausing both directions, we want to have this handle checked soon */ - if(!result && - ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != - (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) ) + if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != + (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) { Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */ - /* This transfer may have been moved in or out of the bundle, update - the corresponding socket callback, if used */ - Curl_updatesocket(data); + /* force a recv/send check of this connection, as the data might've been + read off the socket already */ + data->conn->cselect_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT; + if(data->multi) + Curl_update_timer(data->multi); + } + + if(!data->state.done) + /* This transfer may have been moved in or out of the bundle, update the + corresponding socket callback, if used */ + Curl_updatesocket(data); return result; } @@ -1137,6 +1172,35 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, return result; } +/* + * Wrapper to call functions in Curl_conncache_foreach() + * + * Returns always 0. + */ +static int conn_upkeep(struct connectdata *conn, + void *param) +{ + /* Param is unused. */ + (void)param; + + if(conn->handler->connection_check) { + /* Do a protocol-specific keepalive check on the connection. */ + conn->handler->connection_check(conn, CONNCHECK_KEEPALIVE); + } + + return 0; /* continue iteration */ +} + +static CURLcode upkeep(struct conncache *conn_cache, void *data) +{ + /* Loop over every connection and make connection alive. */ + Curl_conncache_foreach(data, + conn_cache, + data, + conn_upkeep); + return CURLE_OK; +} + /* * Performs connection upkeep for the given session handle. */ @@ -1148,7 +1212,7 @@ CURLcode curl_easy_upkeep(struct Curl_easy *data) if(data->multi_easy) { /* Use the common function to keep connections alive. */ - return Curl_upkeep(&data->multi_easy->conn_cache, data); + return upkeep(&data->multi_easy->conn_cache, data); } else { /* No connections, so just return success */ diff --git a/Utilities/cmcurl/lib/easyif.h b/Utilities/cmcurl/lib/easyif.h index 6ba7e549d7e..eda0d62e5f1 100644 --- a/Utilities/cmcurl/lib/easyif.h +++ b/Utilities/cmcurl/lib/easyif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c index 7121db31c27..2bea145f464 100644 --- a/Utilities/cmcurl/lib/escape.c +++ b/Utilities/cmcurl/lib/escape.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -79,82 +79,83 @@ char *curl_unescape(const char *string, int length) char *curl_easy_escape(struct Curl_easy *data, const char *string, int inlength) { - size_t alloc; - char *ns; - char *testing_ptr = NULL; - size_t newlen; - size_t strindex = 0; size_t length; CURLcode result; + struct dynbuf d; if(inlength < 0) return NULL; - alloc = (inlength?(size_t)inlength:strlen(string)) + 1; - newlen = alloc; + Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH); - ns = malloc(alloc); - if(!ns) - return NULL; + length = (inlength?(size_t)inlength:strlen(string)); + if(!length) + return strdup(""); - length = alloc-1; while(length--) { unsigned char in = *string; /* we need to treat the characters unsigned */ - if(Curl_isunreserved(in)) - /* just copy this */ - ns[strindex++] = in; + if(Curl_isunreserved(in)) { + /* append this */ + if(Curl_dyn_addn(&d, &in, 1)) + return NULL; + } else { /* encode it */ - newlen += 2; /* the size grows with two, since this'll become a %XX */ - if(newlen > alloc) { - alloc *= 2; - testing_ptr = Curl_saferealloc(ns, alloc); - if(!testing_ptr) - return NULL; - ns = testing_ptr; - } - + char encoded[4]; result = Curl_convert_to_network(data, (char *)&in, 1); if(result) { /* Curl_convert_to_network calls failf if unsuccessful */ - free(ns); + Curl_dyn_free(&d); return NULL; } - msnprintf(&ns[strindex], 4, "%%%02X", in); - - strindex += 3; + msnprintf(encoded, sizeof(encoded), "%%%02X", in); + if(Curl_dyn_add(&d, encoded)) + return NULL; } string++; } - ns[strindex] = 0; /* terminate it */ - return ns; + + return Curl_dyn_ptr(&d); } /* * Curl_urldecode() URL decodes the given string. * - * Optionally detects control characters (byte codes lower than 32) in the - * data and rejects such data. - * * Returns a pointer to a malloced string in *ostring with length given in * *olen. If length == 0, the length is assumed to be strlen(string). * * 'data' can be set to NULL but then this function can't convert network * data to host for non-ascii. + * + * ctrl options: + * - REJECT_NADA: accept everything + * - REJECT_CTRL: rejects control characters (byte codes lower than 32) in + * the data + * - REJECT_ZERO: rejects decoded zero bytes + * + * The values for the enum starts at 2, to make the assert detect legacy + * invokes that used TRUE/FALSE (0 and 1). */ + CURLcode Curl_urldecode(struct Curl_easy *data, const char *string, size_t length, char **ostring, size_t *olen, - bool reject_ctrl) + enum urlreject ctrl) { - size_t alloc = (length?length:strlen(string)) + 1; - char *ns = malloc(alloc); + size_t alloc; + char *ns; size_t strindex = 0; unsigned long hex; CURLcode result = CURLE_OK; + DEBUGASSERT(string); + DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */ + + alloc = (length?length:strlen(string)) + 1; + ns = malloc(alloc); + if(!ns) return CURLE_OUT_OF_MEMORY; @@ -186,7 +187,8 @@ CURLcode Curl_urldecode(struct Curl_easy *data, alloc -= 2; } - if(reject_ctrl && (in < 0x20)) { + if(((ctrl == REJECT_CTRL) && (in < 0x20)) || + ((ctrl == REJECT_ZERO) && (in == 0))) { free(ns); return CURLE_URL_MALFORMAT; } @@ -220,7 +222,7 @@ char *curl_easy_unescape(struct Curl_easy *data, const char *string, size_t inputlen = length; size_t outputlen; CURLcode res = Curl_urldecode(data, string, inputlen, &str, &outputlen, - FALSE); + REJECT_NADA); if(res) return NULL; diff --git a/Utilities/cmcurl/lib/escape.h b/Utilities/cmcurl/lib/escape.h index d8bbe5cb0c1..586db7e672a 100644 --- a/Utilities/cmcurl/lib/escape.h +++ b/Utilities/cmcurl/lib/escape.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,9 +25,16 @@ * allocated string or NULL if an error occurred. */ bool Curl_isunreserved(unsigned char in); + +enum urlreject { + REJECT_NADA = 2, + REJECT_CTRL, + REJECT_ZERO +}; + CURLcode Curl_urldecode(struct Curl_easy *data, const char *string, size_t length, char **ostring, size_t *olen, - bool reject_crlf); + enum urlreject ctrl); #endif /* HEADER_CURL_ESCAPE_H */ diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c index d349cd9241c..cd3e49c3394 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -144,7 +144,7 @@ static CURLcode file_connect(struct connectdata *conn, bool *done) size_t real_path_len; CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &real_path, - &real_path_len, FALSE); + &real_path_len, REJECT_ZERO); if(result) return result; diff --git a/Utilities/cmcurl/lib/file.h b/Utilities/cmcurl/lib/file.h index 20828ad4a9b..f6b74a7f723 100644 --- a/Utilities/cmcurl/lib/file.h +++ b/Utilities/cmcurl/lib/file.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c index 429d479da5d..1cab2c5c25e 100644 --- a/Utilities/cmcurl/lib/formdata.c +++ b/Utilities/cmcurl/lib/formdata.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -123,11 +123,11 @@ AddHttpPost(char *name, size_t namelength, * parent_form_info is NULL. * ***************************************************************************/ -static FormInfo * AddFormInfo(char *value, - char *contenttype, - FormInfo *parent_form_info) +static struct FormInfo *AddFormInfo(char *value, + char *contenttype, + struct FormInfo *parent_form_info) { - FormInfo *form_info; + struct FormInfo *form_info; form_info = calloc(1, sizeof(struct FormInfo)); if(form_info) { if(value) @@ -204,7 +204,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, struct curl_httppost **last_post, va_list params) { - FormInfo *first_form, *current_form, *form = NULL; + struct FormInfo *first_form, *current_form, *form = NULL; CURLFORMcode return_value = CURL_FORMADD_OK; const char *prevtype = NULL; struct curl_httppost *post = NULL; @@ -521,7 +521,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if(CURL_FORMADD_OK != return_value) { /* On error, free allocated fields for all nodes of the FormInfo linked list without deallocating nodes. List nodes are deallocated later on */ - FormInfo *ptr; + struct FormInfo *ptr; for(ptr = first_form; ptr != NULL; ptr = ptr->more) { if(ptr->name_alloc) { Curl_safefree(ptr->name); @@ -602,7 +602,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, /* Note that there's small risk that form->name is NULL here if the app passed in a bad combo, so we better check for that first. */ if(form->name) { - /* copy name (without strdup; possibly not nul-terminated) */ + /* copy name (without strdup; possibly not null-terminated) */ form->name = Curl_memdup(form->name, form->namelength? form->namelength: strlen(form->name) + 1); @@ -650,7 +650,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, /* On error, free allocated fields for nodes of the FormInfo linked list which are not already owned by the httppost linked list without deallocating nodes. List nodes are deallocated later on */ - FormInfo *ptr; + struct FormInfo *ptr; for(ptr = form; ptr != NULL; ptr = ptr->more) { if(ptr->name_alloc) { Curl_safefree(ptr->name); @@ -676,7 +676,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, fields given that these have either been deallocated or are owned now by the httppost linked list */ while(first_form) { - FormInfo *ptr = first_form->more; + struct FormInfo *ptr = first_form->more; free(first_form); first_form = ptr; } @@ -728,14 +728,10 @@ int curl_formget(struct curl_httppost *form, void *arg, if(!nread) break; - switch(nread) { - default: - if(append(arg, buffer, nread) != nread) - result = CURLE_READ_ERROR; - break; - case CURL_READFUNC_ABORT: - case CURL_READFUNC_PAUSE: - break; + if(nread > sizeof(buffer) || append(arg, buffer, nread) != nread) { + result = CURLE_READ_ERROR; + if(nread == CURL_READFUNC_ABORT) + result = CURLE_ABORTED_BY_CALLBACK; } } @@ -775,7 +771,7 @@ void curl_formfree(struct curl_httppost *form) } -/* Set mime part name, taking care of non nul-terminated name string. */ +/* Set mime part name, taking care of non null-terminated name string. */ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) { char *zname; diff --git a/Utilities/cmcurl/lib/formdata.h b/Utilities/cmcurl/lib/formdata.h index cb20805f527..3766d38f2b9 100644 --- a/Utilities/cmcurl/lib/formdata.h +++ b/Utilities/cmcurl/lib/formdata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,7 +27,7 @@ #ifndef CURL_DISABLE_MIME /* used by FormAdd for temporary storage */ -typedef struct FormInfo { +struct FormInfo { char *name; bool name_alloc; size_t namelength; @@ -45,7 +45,7 @@ typedef struct FormInfo { char *userp; /* pointer for the read callback */ struct curl_slist *contentheader; struct FormInfo *more; -} FormInfo; +}; CURLcode Curl_getformdata(struct Curl_easy *data, curl_mimepart *, diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 53510f82117..cfd70a6b6ac 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -55,7 +55,6 @@ #include "transfer.h" #include "escape.h" #include "http.h" /* for HTTP proxy tunnel stuff */ -#include "socks.h" #include "ftp.h" #include "fileinfo.h" #include "ftplistparser.h" @@ -78,6 +77,7 @@ #include "warnless.h" #include "http_proxy.h" #include "non-ascii.h" +#include "socks.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -113,7 +113,7 @@ static CURLcode ftp_parse_url_path(struct connectdata *conn); static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done); #ifndef CURL_DISABLE_VERBOSE_STRINGS static void ftp_pasv_verbose(struct connectdata *conn, - Curl_addrinfo *ai, + struct Curl_addrinfo *ai, char *newhost, /* ascii version */ int port); #endif @@ -132,13 +132,11 @@ static CURLcode ftp_connect(struct connectdata *conn, bool *done); static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection); static CURLcode ftp_do_more(struct connectdata *conn, int *completed); static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done); -static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); -static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); +static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks); +static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks); static CURLcode ftp_doing(struct connectdata *conn, bool *dophase_done); -static CURLcode ftp_setup_connection(struct connectdata * conn); +static CURLcode ftp_setup_connection(struct connectdata *conn); static CURLcode init_wc_data(struct connectdata *conn); static CURLcode wc_statemach(struct connectdata *conn); @@ -223,6 +221,9 @@ static void close_secondarysocket(struct connectdata *conn) conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; } conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; +#ifndef CURL_DISABLE_PROXY + conn->bits.proxy_ssl_connected[SECONDARYSOCKET] = FALSE; +#endif } /* @@ -255,18 +256,6 @@ static void freedirs(struct ftp_conn *ftpc) Curl_safefree(ftpc->newhost); } -/* Returns non-zero if the given string contains CR (\r) or LF (\n), - which are not allowed within RFC 959 . - Note: The input string is in the client's encoding which might - not be ASCII, so escape sequences \r & \n must be used instead - of hex values 0x0d & 0x0a. -*/ -static bool isBadFtpString(const char *string) -{ - return ((NULL != strchr(string, '\r')) || - (NULL != strchr(string, '\n'))) ? TRUE : FALSE; -} - /*********************************************************************** * * AcceptServerConnect() @@ -305,7 +294,7 @@ static CURLcode AcceptServerConnect(struct connectdata *conn) conn->sock[SECONDARYSOCKET] = s; (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ - conn->sock_accepted[SECONDARYSOCKET] = TRUE; + conn->bits.sock_accepted = TRUE; if(data->set.fsockopt) { int error = 0; @@ -348,7 +337,7 @@ static timediff_t ftp_timeleft_accept(struct Curl_easy *data) now = Curl_now(); /* check if the generic timeout possibly is set shorter */ - other = Curl_timeleft(data, &now, FALSE); + other = Curl_timeleft(data, &now, FALSE); if(other && (other < timeout_ms)) /* note that this also works fine for when other happens to be negative due to it already having elapsed */ @@ -382,7 +371,7 @@ static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received) struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; int result; - time_t timeout_ms; + timediff_t timeout_ms; ssize_t nread; int ftpcode; @@ -400,7 +389,7 @@ static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received) if(pp->cache_size && pp->cache && pp->cache[0] > '3') { /* Data connection could not be established, let's return */ infof(data, "There is negative response in cache while serv connect\n"); - Curl_GetFTPResponse(&nread, conn, &ftpcode); + (void)Curl_GetFTPResponse(&nread, conn, &ftpcode); return CURLE_FTP_ACCEPT_FAILED; } @@ -422,7 +411,7 @@ static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received) } else if(result & CURL_CSELECT_IN) { infof(data, "Ctrl conn has data while waiting for data conn\n"); - Curl_GetFTPResponse(&nread, conn, &ftpcode); + (void)Curl_GetFTPResponse(&nread, conn, &ftpcode); if(ftpcode/100 > 3) return CURLE_FTP_ACCEPT_FAILED; @@ -493,7 +482,7 @@ static CURLcode InitiateTransfer(struct connectdata *conn) static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected) { struct Curl_easy *data = conn->data; - time_t timeout_ms; + timediff_t timeout_ms; CURLcode result = CURLE_OK; *connected = FALSE; @@ -525,7 +514,7 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected) } else { /* Add timeout to multi handle and break out of the loop */ - if(!result && *connected == FALSE) { + if(*connected == FALSE) { Curl_expire(data, data->set.accepttimeout > 0 ? data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, 0); } @@ -565,10 +554,8 @@ static CURLcode ftp_readresp(curl_socket_t sockfd, #ifdef HAVE_GSSAPI char * const buf = data->state.buffer; #endif - CURLcode result = CURLE_OK; int code; - - result = Curl_pp_readresp(sockfd, pp, &code, size); + CURLcode result = Curl_pp_readresp(sockfd, pp, &code, size); #if defined(HAVE_GSSAPI) /* handle the security-oriented responses 6xx ***/ @@ -648,8 +635,8 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ while(!*ftpcode && !result) { /* check and reset timeout value every lap */ - time_t timeout = Curl_pp_state_timeout(pp, FALSE); - time_t interval_ms; + timediff_t timeout = Curl_pp_state_timeout(pp, FALSE); + timediff_t interval_ms; if(timeout <= 0) { failf(data, "FTP response timeout"); @@ -789,9 +776,8 @@ static void _state(struct connectdata *conn, static CURLcode ftp_state_user(struct connectdata *conn) { CURLcode result; - struct FTP *ftp = conn->data->req.protop; /* send USER */ - PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:""); + PPSENDF(&conn->proto.ftpc.pp, "USER %s", conn->user?conn->user:""); state(conn, FTP_USER); conn->data->state.ftp_trying_alternative = FALSE; @@ -812,28 +798,27 @@ static CURLcode ftp_state_pwd(struct connectdata *conn) /* For the FTP "protocol connect" and "doing" phases only */ static int ftp_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { - return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks); + return Curl_pp_getsock(&conn->proto.ftpc.pp, socks); } /* For the FTP "DO_MORE" phase only */ -static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks) { struct ftp_conn *ftpc = &conn->proto.ftpc; - if(!numsocks) - return GETSOCK_BLANK; - /* When in DO_MORE state, we could be either waiting for us to connect to a * remote site, or we could wait for that site to connect to us. Or just * handle ordinary commands. */ + if(SOCKS_STATE(conn->cnnct.state)) + return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET); + if(FTP_STOP == ftpc->state) { int bits = GETSOCK_READSOCK(0); + bool any = FALSE; /* if stopped and still in this state, then we're also waiting for a connect on the secondary connection */ @@ -848,17 +833,18 @@ static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks, if(conn->tempsock[i] != CURL_SOCKET_BAD) { socks[s] = conn->tempsock[i]; bits |= GETSOCK_WRITESOCK(s++); + any = TRUE; } } } - else { + if(!any) { socks[1] = conn->sock[SECONDARYSOCKET]; bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1); } return bits; } - return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks); + return Curl_pp_getsock(&conn->proto.ftpc.pp, socks); } /* This is called after the FTP_QUOTE state is passed. @@ -876,6 +862,10 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) /* already done and fine */ result = ftp_state_mdtm(conn); else { + /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */ + DEBUGASSERT((conn->data->set.ftp_filemethod != FTPFILE_NOCWD) || + !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')); + ftpc->count2 = 0; /* count2 counts failed CWDs */ /* count3 is set to allow a MKD to fail once. In the case when first CWD @@ -883,10 +873,9 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) dir) this then allows for a second try to CWD to it */ ftpc->count3 = (conn->data->set.ftp_create_missing_dirs == 2)?1:0; - if((conn->data->set.ftp_filemethod == FTPFILE_NOCWD) && !ftpc->cwdcount) - /* No CWD necessary */ - result = ftp_state_mdtm(conn); - else if(conn->bits.reuse && ftpc->entrypath) { + if(conn->bits.reuse && ftpc->entrypath && + /* no need to go to entrypath when we have an absolute path */ + !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) { /* This is a re-used connection. Since we change directory to where the transfer is taking place, we must first get back to the original dir where we ended up after login: */ @@ -926,10 +915,10 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, struct ftp_conn *ftpc = &conn->proto.ftpc; struct Curl_easy *data = conn->data; curl_socket_t portsock = CURL_SOCKET_BAD; - char myhost[256] = ""; + char myhost[MAX_IPADR_LEN + 1] = ""; struct Curl_sockaddr_storage ss; - Curl_addrinfo *res, *ai; + struct Curl_addrinfo *res, *ai; curl_socklen_t sslen; char hbuf[NI_MAXHOST]; struct sockaddr *sa = (struct sockaddr *)&ss; @@ -937,9 +926,8 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, #ifdef ENABLE_IPV6 struct sockaddr_in6 * const sa6 = (void *)sa; #endif - char tmp[1024]; static const char mode[][5] = { "EPRT", "PORT" }; - int rc; + enum resolve_t rc; int error; char *host = NULL; char *string_ftpport = data->set.str[STRING_FTPPORT]; @@ -1055,6 +1043,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, } /* data->set.ftpport */ if(!host) { + const char *r; /* not an interface and not a host name, get default by extracting the IP from the control connection */ sslen = sizeof(ss); @@ -1067,13 +1056,15 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, switch(sa->sa_family) { #ifdef ENABLE_IPV6 case AF_INET6: - Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf)); + r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf)); break; #endif default: - Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); + r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); break; } + if(!r) + return CURLE_FTP_PORT_FAILED; host = hbuf; /* use this host name */ possibly_non_local = FALSE; /* we know it is local now */ } @@ -1252,8 +1243,10 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, break; } if(PORT == fcmd) { + /* large enough for [IP address],[num],[num] */ + char target[sizeof(myhost) + 20]; char *source = myhost; - char *dest = tmp; + char *dest = target; /* translate x.x.x.x to x,x,x,x */ while(source && *source) { @@ -1267,7 +1260,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, *dest = 0; msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff)); - result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp); + result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], target); if(result) { failf(data, "Failure sending PORT command: %s", curl_easy_strerror(result)); @@ -1308,7 +1301,7 @@ static CURLcode ftp_state_use_pasv(struct connectdata *conn) struct ftp_conn *ftpc = &conn->proto.ftpc; CURLcode result = CURLE_OK; /* - Here's the excecutive summary on what to do: + Here's the executive summary on what to do: PASV is RFC959, expect: 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) @@ -1445,31 +1438,37 @@ static CURLcode ftp_state_list(struct connectdata *conn) servers either... */ /* - if FTPFILE_NOCWD was specified, we are currently in - the user's home directory, so we should add the path + if FTPFILE_NOCWD was specified, we should add the path as argument for the LIST / NLST / or custom command. Whether the server will support this, is uncertain. The other ftp_filemethods will CWD into dir/dir/ first and then just do LIST (in that case: nothing to do here) */ - char *cmd, *lstArg, *slashPos; - const char *inpath = ftp->path; - - lstArg = NULL; - if((data->set.ftp_filemethod == FTPFILE_NOCWD) && - inpath && inpath[0] && strchr(inpath, '/')) { - size_t n = strlen(inpath); - - /* Check if path does not end with /, as then we cut off the file part */ - if(inpath[n - 1] != '/') { - /* chop off the file part if format is dir/dir/file */ - slashPos = strrchr(inpath, '/'); - n = slashPos - inpath; - } - result = Curl_urldecode(data, inpath, n, &lstArg, NULL, TRUE); + char *lstArg = NULL; + char *cmd; + + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) { + /* url-decode before evaluation: e.g. paths starting/ending with %2f */ + const char *slashPos = NULL; + char *rawPath = NULL; + result = Curl_urldecode(data, ftp->path, 0, &rawPath, NULL, REJECT_CTRL); if(result) return result; + + slashPos = strrchr(rawPath, '/'); + if(slashPos) { + /* chop off the file part if format is dir/file otherwise remove + the trailing slash for dir/dir/ except for absolute path / */ + size_t n = slashPos - rawPath; + if(n == 0) + ++n; + + lstArg = rawPath; + lstArg[n] = '\0'; + } + else + free(rawPath); } cmd = aprintf("%s%s%s", @@ -1478,15 +1477,12 @@ static CURLcode ftp_state_list(struct connectdata *conn) (data->set.ftp_list_only?"NLST":"LIST"), lstArg? " ": "", lstArg? lstArg: ""); + free(lstArg); - if(!cmd) { - free(lstArg); + if(!cmd) return CURLE_OUT_OF_MEMORY; - } result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd); - - free(lstArg); free(cmd); if(result) @@ -1499,24 +1495,14 @@ static CURLcode ftp_state_list(struct connectdata *conn) static CURLcode ftp_state_retr_prequote(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* We've sent the TYPE, now we must send the list of prequote strings */ - - result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE); - - return result; + return ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE); } static CURLcode ftp_state_stor_prequote(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* We've sent the TYPE, now we must send the list of prequote strings */ - - result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE); - - return result; + return ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE); } static CURLcode ftp_state_type(struct connectdata *conn) @@ -1781,7 +1767,11 @@ static CURLcode ftp_epsv_disable(struct connectdata *conn) { CURLcode result = CURLE_OK; - if(conn->bits.ipv6 && !(conn->bits.tunnel_proxy || conn->bits.socksproxy)) { + if(conn->bits.ipv6 +#ifndef CURL_DISABLE_PROXY + && !(conn->bits.tunnel_proxy || conn->bits.socksproxy) +#endif + ) { /* We can't disable EPSV when doing IPv6, so this is instead a fail */ failf(conn->data, "Failed EPSV attempt, exiting\n"); return CURLE_WEIRD_SERVER_REPLY; @@ -1806,9 +1796,10 @@ static char *control_address(struct connectdata *conn) If a proxy tunnel is used, returns the original host name instead, because the effective control connection address is the proxy address, not the ftp host. */ +#ifndef CURL_DISABLE_PROXY if(conn->bits.tunnel_proxy || conn->bits.socksproxy) return conn->host.name; - +#endif return conn->ip_addr_str; } @@ -1819,7 +1810,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, CURLcode result; struct Curl_easy *data = conn->data; struct Curl_dns_entry *addr = NULL; - int rc; + enum resolve_t rc; unsigned short connectport; /* the local port connect() should use! */ char *str = &data->state.buffer[4]; /* start on the first letter */ @@ -1925,6 +1916,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, return CURLE_FTP_WEIRD_PASV_REPLY; } +#ifndef CURL_DISABLE_PROXY if(conn->bits.proxy) { /* * This connection uses a proxy and we need to connect to the proxy again @@ -1947,7 +1939,9 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, return CURLE_COULDNT_RESOLVE_PROXY; } } - else { + else +#endif + { /* normal, direct, ftp connection */ rc = Curl_resolv(conn, ftpc->newhost, ftpc->newport, FALSE, &addr); if(rc == CURLRESOLV_PENDING) @@ -2052,13 +2046,11 @@ static CURLcode ftp_state_mdtm_resp(struct connectdata *conn, &year, &month, &day, &hour, &minute, &second)) { /* we have a time, reformat it */ char timebuf[24]; - time_t secs = time(NULL); - msnprintf(timebuf, sizeof(timebuf), "%04d%02d%02d %02d:%02d:%02d GMT", year, month, day, hour, minute, second); /* now, convert this into a time() value: */ - data->info.filetime = curl_getdate(timebuf, &secs); + data->info.filetime = Curl_getdate_capped(timebuf); } #ifdef CURL_FTP_HTTPSTYLE_HEAD @@ -2261,9 +2253,25 @@ static CURLcode ftp_state_size_resp(struct connectdata *conn, char *buf = data->state.buffer; /* get the size from the ascii string: */ - if(ftpcode == 213) + if(ftpcode == 213) { + /* To allow servers to prepend "rubbish" in the response string, we scan + for all the digits at the end of the response and parse only those as a + number. */ + char *start = &buf[4]; + char *fdigit = strchr(start, '\r'); + if(fdigit) { + do + fdigit--; + while(ISDIGIT(*fdigit) && (fdigit > start)); + if(!ISDIGIT(*fdigit)) + fdigit++; + } + else + fdigit = start; /* ignores parsing errors, which will make the size remain unknown */ - (void)curlx_strtoofft(buf + 4, NULL, 0, &filesize); + (void)curlx_strtoofft(fdigit, NULL, 0, &filesize); + + } if(instate == FTP_SIZE) { #ifdef CURL_FTP_HTTPSTYLE_HEAD @@ -2527,7 +2535,6 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; - struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; (void)instate; /* no use for this yet */ @@ -2535,7 +2542,7 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn, if((ftpcode == 331) && (ftpc->state == FTP_USER)) { /* 331 Password required for ... (the server requires to send the user's password too) */ - PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:""); + PPSENDF(&ftpc->pp, "PASS %s", conn->passwd?conn->passwd:""); state(conn, FTP_PASS); } else if(ftpcode/100 == 2) { @@ -2643,9 +2650,12 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) #endif if(data->set.use_ssl && - (!conn->ssl[FIRSTSOCKET].use || - (conn->bits.proxy_ssl_connected[FIRSTSOCKET] && - !conn->proxy_ssl[FIRSTSOCKET].use))) { + (!conn->ssl[FIRSTSOCKET].use +#ifndef CURL_DISABLE_PROXY + || (conn->bits.proxy_ssl_connected[FIRSTSOCKET] && + !conn->proxy_ssl[FIRSTSOCKET].use) +#endif + )) { /* We don't have a SSL/TLS connection yet, but FTPS is requested. Try a FTPS connection now */ @@ -2817,7 +2827,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) store++; ptr++; } - *store = '\0'; /* zero terminate */ + *store = '\0'; /* null-terminate */ } if(entry_extracted) { /* If the path name does not look like an absolute path (i.e.: it @@ -2881,7 +2891,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) ptr++; for(store = os; *ptr && *ptr != ' ';) *store++ = *ptr++; - *store = '\0'; /* zero terminate */ + *store = '\0'; /* null-terminate */ /* Check for special servers here. */ @@ -3134,7 +3144,8 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, ssize_t nread; int ftpcode; CURLcode result = CURLE_OK; - char *path = NULL; + char *rawPath = NULL; + size_t pathLen = 0; if(!ftp) return CURLE_OK; @@ -3172,9 +3183,6 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, break; } - /* now store a copy of the directory we are in */ - free(ftpc->prevpath); - if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { Curl_set_in_callback(data, true); @@ -3185,41 +3193,42 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, } if(!result) - /* get the "raw" path */ - result = Curl_urldecode(data, ftp->path, 0, &path, NULL, TRUE); + /* get the url-decoded "raw" path */ + result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, + REJECT_CTRL); if(result) { /* We can limp along anyway (and should try to since we may already be in * the error path) */ ftpc->ctl_valid = FALSE; /* mark control connection as bad */ connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ + free(ftpc->prevpath); ftpc->prevpath = NULL; /* no path remembering */ } - else { - size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */ - size_t dlen = strlen(path)-flen; - if(!ftpc->cwdfail) { - ftpc->prevmethod = data->set.ftp_filemethod; - if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) { - ftpc->prevpath = path; - if(flen) - /* if 'path' is not the whole string */ - ftpc->prevpath[dlen] = 0; /* terminate */ + else { /* remember working directory for connection reuse */ + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) + free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */ + else { + free(ftpc->prevpath); + + if(!ftpc->cwdfail) { + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + pathLen = 0; /* relative path => working directory is FTP home */ + else + pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */ + + rawPath[pathLen] = '\0'; + ftpc->prevpath = rawPath; } else { - free(path); - /* we never changed dir */ - ftpc->prevpath = strdup(""); - if(!ftpc->prevpath) - return CURLE_OUT_OF_MEMORY; + free(rawPath); + ftpc->prevpath = NULL; /* no path */ } - if(ftpc->prevpath) - infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath); - } - else { - ftpc->prevpath = NULL; /* no path */ - free(path); } + + if(ftpc->prevpath) + infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath); } + /* free the dir tree and file parts */ freedirs(ftpc); @@ -3242,9 +3251,9 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, } if(conn->ssl[SECONDARYSOCKET].use) { - /* The secondary socket is using SSL so we must close down that part - first before we close the socket for real */ - Curl_ssl_close(conn, SECONDARYSOCKET); + /* The secondary socket used SSL so we must close down that part first + before we close the socket for real */ + result = Curl_ssl_shutdown(conn, SECONDARYSOCKET); /* Note that we keep "use" set to TRUE since that (next) connection is still requested to use SSL */ @@ -3260,7 +3269,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, * data has been transferred. This happens when doing through NATs etc that * abandon old silent connections. */ - long old_time = pp->response_time; + timediff_t old_time = pp->response_time; pp->response_time = 60*1000; /* give it only a minute for now */ pp->response = Curl_now(); /* timeout relative now */ @@ -3453,7 +3462,7 @@ static CURLcode ftp_nb_type(struct connectdata *conn, #ifndef CURL_DISABLE_VERBOSE_STRINGS static void ftp_pasv_verbose(struct connectdata *conn, - Curl_addrinfo *ai, + struct Curl_addrinfo *ai, char *newhost, /* ascii version */ int port) { @@ -3511,6 +3520,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) } } +#ifndef CURL_DISABLE_PROXY result = Curl_proxy_connect(conn, SECONDARYSOCKET); if(result) return result; @@ -3521,7 +3531,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) if(conn->bits.tunnel_proxy && conn->bits.httpproxy && Curl_connect_ongoing(conn)) return result; - +#endif if(ftpc->state) { /* already in a state so skip the initial commands. @@ -3532,14 +3542,13 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) /* if we got an error or if we don't wait for a data connection return immediately */ - if(result || (ftpc->wait_data_conn != TRUE)) + if(result || !ftpc->wait_data_conn) return result; - if(ftpc->wait_data_conn) - /* if we reach the end of the FTP state machine here, *complete will be - TRUE but so is ftpc->wait_data_conn, which says we need to wait for - the data connection and therefore we're not actually complete */ - *completep = 0; + /* if we reach the end of the FTP state machine here, *complete will be + TRUE but so is ftpc->wait_data_conn, which says we need to wait for the + data connection and therefore we're not actually complete */ + *completep = 0; } if(ftp->transfer <= FTPTRANSFER_INFO) { @@ -3573,13 +3582,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) return result; result = ftp_multi_statemach(conn, &complete); - if(ftpc->wait_data_conn) - /* if we reach the end of the FTP state machine here, *complete will be - TRUE but so is ftpc->wait_data_conn, which says we need to wait for - the data connection and therefore we're not actually complete */ - *completep = 0; - else - *completep = (int)complete; + /* ftpc->wait_data_conn is always false here */ + *completep = (int)complete; } else { /* download */ @@ -3619,10 +3623,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) return result; } - if(!result && (ftp->transfer != FTPTRANSFER_BODY)) - /* no data to transfer. FIX: it feels like a kludge to have this here - too! */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + /* no data to transfer */ + Curl_setup_transfer(data, -1, -1, FALSE, -1); if(!ftpc->wait_data_conn) { /* no waiting for the data connection so this is now complete */ @@ -4100,186 +4102,142 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) /* the ftp struct is already inited in ftp_connect() */ struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; - const char *slash_pos; /* position of the first '/' char in curpos */ - const char *path_to_use = ftp->path; - const char *cur_pos; - const char *filename = NULL; - - cur_pos = path_to_use; /* current position in path. point at the begin of - next path component */ + const char *slashPos = NULL; + const char *fileName = NULL; + CURLcode result = CURLE_OK; + char *rawPath = NULL; /* url-decoded "raw" path */ + size_t pathLen = 0; ftpc->ctl_valid = FALSE; ftpc->cwdfail = FALSE; - switch(data->set.ftp_filemethod) { - case FTPFILE_NOCWD: - /* fastest, but less standard-compliant */ - - /* - The best time to check whether the path is a file or directory is right - here. so: + /* url-decode ftp path before further evaluation */ + result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL); + if(result) + return result; - the first condition in the if() right here, is there just in case - someone decides to set path to NULL one day - */ - if(path_to_use[0] && - (path_to_use[strlen(path_to_use) - 1] != '/') ) - filename = path_to_use; /* this is a full file path */ - /* - else { - ftpc->file is not used anywhere other than for operations on a file. - In other words, never for directory operations. - So we can safely leave filename as NULL here and use it as a - argument in dir/file decisions. - } - */ - break; + switch(data->set.ftp_filemethod) { + case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ - case FTPFILE_SINGLECWD: - /* get the last slash */ - if(!path_to_use[0]) { - /* no dir, no file */ - ftpc->dirdepth = 0; + if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) + fileName = rawPath; /* this is a full file path */ + /* + else: ftpc->file is not used anywhere other than for operations on + a file. In other words, never for directory operations. + So we can safely leave filename as NULL here and use it as a + argument in dir/file decisions. + */ break; - } - slash_pos = strrchr(cur_pos, '/'); - if(slash_pos || !*cur_pos) { - size_t dirlen = slash_pos-cur_pos; - CURLcode result; - ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) - return CURLE_OUT_OF_MEMORY; + case FTPFILE_SINGLECWD: + slashPos = strrchr(rawPath, '/'); + if(slashPos) { + /* get path before last slash, except for / */ + size_t dirlen = slashPos - rawPath; + if(dirlen == 0) + dirlen++; + + ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } - if(!dirlen) - dirlen++; + ftpc->dirs[0] = calloc(1, dirlen + 1); + if(!ftpc->dirs[0]) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } - result = Curl_urldecode(conn->data, slash_pos ? cur_pos : "/", - slash_pos ? dirlen : 1, - &ftpc->dirs[0], NULL, - TRUE); - if(result) { - freedirs(ftpc); - return result; + strncpy(ftpc->dirs[0], rawPath, dirlen); + ftpc->dirdepth = 1; /* we consider it to be a single dir */ + fileName = slashPos + 1; /* rest is file name */ } - ftpc->dirdepth = 1; /* we consider it to be a single dir */ - filename = slash_pos ? slash_pos + 1 : cur_pos; /* rest is file name */ - } - else - filename = cur_pos; /* this is a file name only */ - break; + else + fileName = rawPath; /* file name only (or empty) */ + break; - default: /* allow pretty much anything */ - case FTPFILE_MULTICWD: - ftpc->dirdepth = 0; - ftpc->diralloc = 5; /* default dir depth to allocate */ - ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) - return CURLE_OUT_OF_MEMORY; + default: /* allow pretty much anything */ + case FTPFILE_MULTICWD: { + /* current position: begin of next path component */ + const char *curPos = rawPath; + + int dirAlloc = 0; /* number of entries allocated for the 'dirs' array */ + const char *str = rawPath; + for(; *str != 0; ++str) + if (*str == '/') + ++dirAlloc; + + if(dirAlloc > 0) { + ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } + + /* parse the URL path into separate path components */ + while((slashPos = strchr(curPos, '/')) != NULL) { + size_t compLen = slashPos - curPos; + + /* path starts with a slash: add that as a directory */ + if((compLen == 0) && (ftpc->dirdepth == 0)) + ++compLen; - /* we have a special case for listing the root dir only */ - if(!strcmp(path_to_use, "/")) { - cur_pos++; /* make it point to the zero byte */ - ftpc->dirs[0] = strdup("/"); - ftpc->dirdepth++; - } - else { - /* parse the URL path into separate path components */ - while((slash_pos = strchr(cur_pos, '/')) != NULL) { - /* 1 or 0 pointer offset to indicate absolute directory */ - ssize_t absolute_dir = ((cur_pos - ftp->path > 0) && - (ftpc->dirdepth == 0))?1:0; - - /* seek out the next path component */ - if(slash_pos-cur_pos) { /* we skip empty path components, like "x//y" since the FTP command CWD requires a parameter and a non-existent parameter a) doesn't work on many servers and b) has no effect on the others. */ - size_t len = slash_pos - cur_pos + absolute_dir; - CURLcode result = - Curl_urldecode(conn->data, cur_pos - absolute_dir, len, - &ftpc->dirs[ftpc->dirdepth], NULL, - TRUE); - if(result) { - freedirs(ftpc); - return result; - } - } - else { - cur_pos = slash_pos + 1; /* jump to the rest of the string */ - if(!ftpc->dirdepth) { - /* path starts with a slash, add that as a directory */ - ftpc->dirs[ftpc->dirdepth] = strdup("/"); - if(!ftpc->dirs[ftpc->dirdepth++]) { /* run out of memory ... */ - failf(data, "no memory"); - freedirs(ftpc); + if(compLen > 0) { + char *comp = calloc(1, compLen + 1); + if(!comp) { + free(rawPath); return CURLE_OUT_OF_MEMORY; } + strncpy(comp, curPos, compLen); + ftpc->dirs[ftpc->dirdepth++] = comp; } - continue; - } - - cur_pos = slash_pos + 1; /* jump to the rest of the string */ - if(++ftpc->dirdepth >= ftpc->diralloc) { - /* enlarge array */ - char **bigger; - ftpc->diralloc *= 2; /* double the size each time */ - bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0])); - if(!bigger) { - freedirs(ftpc); - return CURLE_OUT_OF_MEMORY; - } - ftpc->dirs = bigger; + curPos = slashPos + 1; } } + DEBUGASSERT(ftpc->dirdepth <= dirAlloc); + fileName = curPos; /* the rest is the file name (or empty) */ } - filename = cur_pos; /* the rest is the file name */ break; } /* switch */ - if(filename && *filename) { - CURLcode result = - Curl_urldecode(conn->data, filename, 0, &ftpc->file, NULL, TRUE); - - if(result) { - freedirs(ftpc); - return result; - } - } + if(fileName && *fileName) + ftpc->file = strdup(fileName); else - ftpc->file = NULL; /* instead of point to a zero byte, we make it a NULL - pointer */ + ftpc->file = NULL; /* instead of point to a zero byte, + we make it a NULL pointer */ if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) { /* We need a file name when uploading. Return error! */ failf(data, "Uploading to a URL without a file name!"); + free(rawPath); return CURLE_URL_MALFORMAT; } ftpc->cwddone = FALSE; /* default to not done */ - if(ftpc->prevpath) { - /* prevpath is "raw" so we convert the input path before we compare the - strings */ - size_t dlen; - char *path; - CURLcode result = - Curl_urldecode(conn->data, ftp->path, 0, &path, &dlen, TRUE); - if(result) { - freedirs(ftpc); - return result; - } + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) + ftpc->cwddone = TRUE; /* skip CWD for absolute paths */ + else { /* newly created FTP connections are already in entry path */ + const char *oldPath = conn->bits.reuse ? ftpc->prevpath : ""; + if(oldPath) { + size_t n = pathLen; + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + n = 0; /* CWD to entry for relative paths */ + else + n -= ftpc->file?strlen(ftpc->file):0; - dlen -= ftpc->file?strlen(ftpc->file):0; - if((dlen == strlen(ftpc->prevpath)) && - !strncmp(path, ftpc->prevpath, dlen) && - (ftpc->prevmethod == data->set.ftp_filemethod)) { - infof(data, "Request has same path as previous transfer\n"); - ftpc->cwddone = TRUE; + if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) { + infof(data, "Request has same path as previous transfer\n"); + ftpc->cwddone = TRUE; + } } - free(path); } + free(rawPath); return CURLE_OK; } @@ -4401,7 +4359,6 @@ static CURLcode ftp_setup_connection(struct connectdata *conn) char command; *type = 0; /* it was in the middle of the hostname */ command = Curl_raw_toupper(type[6]); - conn->bits.type_set = TRUE; switch(command) { case 'A': /* ASCII mode */ @@ -4423,18 +4380,6 @@ static CURLcode ftp_setup_connection(struct connectdata *conn) /* get some initial data into the ftp struct */ ftp->transfer = FTPTRANSFER_BODY; ftp->downloadsize = 0; - - /* No need to duplicate user+password, the connectdata struct won't change - during a session, but we re-init them here since on subsequent inits - since the conn struct may have changed or been replaced. - */ - ftp->user = conn->user; - ftp->passwd = conn->passwd; - if(isBadFtpString(ftp->user)) - return CURLE_URL_MALFORMAT; - if(isBadFtpString(ftp->passwd)) - return CURLE_URL_MALFORMAT; - conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ return CURLE_OK; diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h index 828d69a21d7..06421c6a40d 100644 --- a/Utilities/cmcurl/lib/ftp.h +++ b/Utilities/cmcurl/lib/ftp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -102,8 +102,6 @@ typedef enum { perhaps the Curl_easy is changed between the times the connection is used. */ struct FTP { - char *user; /* user name string */ - char *passwd; /* password string */ char *path; /* points to the urlpieces struct field */ char *pathalloc; /* if non-NULL a pointer to an allocated path */ @@ -121,8 +119,7 @@ struct ftp_conn { char *entrypath; /* the PWD reply when we logged on */ char **dirs; /* realloc()ed array for path components */ int dirdepth; /* number of entries used in the 'dirs' array */ - int diralloc; /* number of entries allocated for the 'dirs' array */ - char *file; /* decoded file */ + char *file; /* url-decoded file name (or path) */ bool dont_check; /* Set to TRUE to prevent the final (post-transfer) file size and 226/250 status check. It should still read the line, just ignore the result. */ @@ -135,8 +132,7 @@ struct ftp_conn { bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent caching the current directory */ bool wait_data_conn; /* this is set TRUE if data connection is waited */ - char *prevpath; /* conn->path from the previous transfer */ - curl_ftpfile prevmethod; /* ftp method in previous transfer */ + char *prevpath; /* url-decoded conn->path from the previous transfer */ char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a and others (A/I or zero) */ int count1; /* general purpose counter for the state machine */ @@ -154,7 +150,6 @@ struct ftp_conn { connection to */ char *newhost; /* this is the pair to connect the DATA... */ unsigned short newport; /* connection to */ - }; #define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */ diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c index c4eb43732d0..f399a4c272b 100644 --- a/Utilities/cmcurl/lib/ftplistparser.c +++ b/Utilities/cmcurl/lib/ftplistparser.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/ftplistparser.h b/Utilities/cmcurl/lib/ftplistparser.h index 8128887c0b1..b34ae9b63ae 100644 --- a/Utilities/cmcurl/lib/ftplistparser.h +++ b/Utilities/cmcurl/lib/ftplistparser.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/getenv.c b/Utilities/cmcurl/lib/getenv.c index 89d181de3c2..9385b8f677f 100644 --- a/Utilities/cmcurl/lib/getenv.c +++ b/Utilities/cmcurl/lib/getenv.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,25 +27,48 @@ #include "memdebug.h" -static -char *GetEnv(const char *variable) +static char *GetEnv(const char *variable) { #if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) (void)variable; return NULL; -#else -#ifdef WIN32 - char env[MAX_PATH]; /* MAX_PATH is from windef.h */ - char *temp = getenv(variable); - env[0] = '\0'; - if(temp != NULL) - ExpandEnvironmentStringsA(temp, env, sizeof(env)); - return (env[0] != '\0')?strdup(env):NULL; +#elif defined(WIN32) + /* This uses Windows API instead of C runtime getenv() to get the environment + variable since some changes aren't always visible to the latter. #4774 */ + char *buf = NULL; + char *tmp; + DWORD bufsize; + DWORD rc = 1; + const DWORD max = 32768; /* max env var size from MSCRT source */ + + for(;;) { + tmp = realloc(buf, rc); + if(!tmp) { + free(buf); + return NULL; + } + + buf = tmp; + bufsize = rc; + + /* It's possible for rc to be 0 if the variable was found but empty. + Since getenv doesn't make that distinction we ignore it as well. */ + rc = GetEnvironmentVariableA(variable, buf, bufsize); + if(!rc || rc == bufsize || rc > max) { + free(buf); + return NULL; + } + + /* if rc < bufsize then rc is bytes written not including null */ + if(rc < bufsize) + return buf; + + /* else rc is bytes needed, try again */ + } #else char *env = getenv(variable); return (env && env[0])?strdup(env):NULL; #endif -#endif } char *curl_getenv(const char *v) diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c index e118da80d41..6d5bd5fc5b2 100644 --- a/Utilities/cmcurl/lib/getinfo.c +++ b/Utilities/cmcurl/lib/getinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -147,6 +147,33 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, long *to_long; } lptr; +#ifdef DEBUGBUILD + char *timestr = getenv("CURL_TIME"); + if(timestr) { + unsigned long val = strtol(timestr, NULL, 10); + switch(info) { + case CURLINFO_LOCAL_PORT: + *param_longp = (long)val; + return CURLE_OK; + default: + break; + } + } + /* use another variable for this to allow different values */ + timestr = getenv("CURL_DEBUG_SIZE"); + if(timestr) { + unsigned long val = strtol(timestr, NULL, 10); + switch(info) { + case CURLINFO_HEADER_SIZE: + case CURLINFO_REQUEST_SIZE: + *param_longp = (long)val; + return CURLE_OK; + default: + break; + } + } +#endif + switch(info) { case CURLINFO_RESPONSE_CODE: *param_longp = data->info.httpcode; @@ -171,9 +198,11 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, case CURLINFO_SSL_VERIFYRESULT: *param_longp = data->set.ssl.certverifyresult; break; +#ifndef CURL_DISABLE_PROXY case CURLINFO_PROXY_SSL_VERIFYRESULT: *param_longp = data->set.proxy_ssl.certverifyresult; break; +#endif case CURLINFO_REDIRECT_COUNT: *param_longp = data->set.followlocation; break; @@ -212,8 +241,11 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, *param_longp = data->info.conn_local_port; break; case CURLINFO_CONDITION_UNMET: - /* return if the condition prevented the document to get transferred */ - *param_longp = data->info.timecond ? 1L : 0L; + if(data->info.httpcode == 304) + *param_longp = 1L; + else + /* return if the condition prevented the document to get transferred */ + *param_longp = data->info.timecond ? 1L : 0L; break; case CURLINFO_RTSP_CLIENT_CSEQ: *param_longp = data->state.rtsp_next_client_CSeq; @@ -235,6 +267,9 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, case 20: *param_longp = CURL_HTTP_VERSION_2_0; break; + case 30: + *param_longp = CURL_HTTP_VERSION_3; + break; default: *param_longp = CURL_HTTP_VERSION_NONE; break; @@ -243,7 +278,6 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, case CURLINFO_PROTOCOL: *param_longp = data->info.conn_protocol; break; - default: return CURLE_UNKNOWN_OPTION; } @@ -256,6 +290,27 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, curl_off_t *param_offt) { +#ifdef DEBUGBUILD + char *timestr = getenv("CURL_TIME"); + if(timestr) { + unsigned long val = strtol(timestr, NULL, 10); + switch(info) { + case CURLINFO_TOTAL_TIME_T: + case CURLINFO_NAMELOOKUP_TIME_T: + case CURLINFO_CONNECT_TIME_T: + case CURLINFO_APPCONNECT_TIME_T: + case CURLINFO_PRETRANSFER_TIME_T: + case CURLINFO_STARTTRANSFER_TIME_T: + case CURLINFO_REDIRECT_TIME_T: + case CURLINFO_SPEED_DOWNLOAD_T: + case CURLINFO_SPEED_UPLOAD_T: + *param_offt = (curl_off_t)val; + return CURLE_OK; + default: + break; + } + } +#endif switch(info) { case CURLINFO_FILETIME_T: *param_offt = (curl_off_t)data->info.filetime; @@ -267,7 +322,7 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, *param_offt = data->progress.downloaded; break; case CURLINFO_SPEED_DOWNLOAD_T: - *param_offt = data->progress.dlspeed; + *param_offt = data->progress.dlspeed; break; case CURLINFO_SPEED_UPLOAD_T: *param_offt = data->progress.ulspeed; @@ -280,7 +335,7 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN)? data->progress.size_ul:-1; break; - case CURLINFO_TOTAL_TIME_T: + case CURLINFO_TOTAL_TIME_T: *param_offt = data->progress.timespent; break; case CURLINFO_NAMELOOKUP_TIME_T: @@ -301,7 +356,9 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, case CURLINFO_REDIRECT_TIME_T: *param_offt = data->progress.t_redirect; break; - + case CURLINFO_RETRY_AFTER: + *param_offt = data->info.retry_after; + break; default: return CURLE_UNKNOWN_OPTION; } @@ -312,6 +369,27 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info, double *param_doublep) { +#ifdef DEBUGBUILD + char *timestr = getenv("CURL_TIME"); + if(timestr) { + unsigned long val = strtol(timestr, NULL, 10); + switch(info) { + case CURLINFO_TOTAL_TIME: + case CURLINFO_NAMELOOKUP_TIME: + case CURLINFO_CONNECT_TIME: + case CURLINFO_APPCONNECT_TIME: + case CURLINFO_PRETRANSFER_TIME: + case CURLINFO_STARTTRANSFER_TIME: + case CURLINFO_REDIRECT_TIME: + case CURLINFO_SPEED_DOWNLOAD: + case CURLINFO_SPEED_UPLOAD: + *param_doublep = (double)val; + return CURLE_OK; + default: + break; + } + } +#endif switch(info) { case CURLINFO_TOTAL_TIME: *param_doublep = DOUBLE_SECS(data->progress.timespent); @@ -332,13 +410,13 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info, *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer); break; case CURLINFO_SIZE_UPLOAD: - *param_doublep = (double)data->progress.uploaded; + *param_doublep = (double)data->progress.uploaded; break; case CURLINFO_SIZE_DOWNLOAD: *param_doublep = (double)data->progress.downloaded; break; case CURLINFO_SPEED_DOWNLOAD: - *param_doublep = (double)data->progress.dlspeed; + *param_doublep = (double)data->progress.dlspeed; break; case CURLINFO_SPEED_UPLOAD: *param_doublep = (double)data->progress.ulspeed; diff --git a/Utilities/cmcurl/lib/getinfo.h b/Utilities/cmcurl/lib/getinfo.h index aecf717f754..8d2af4266dd 100644 --- a/Utilities/cmcurl/lib/getinfo.h +++ b/Utilities/cmcurl/lib/getinfo.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c index b296c62d199..b4811b28944 100644 --- a/Utilities/cmcurl/lib/gopher.c +++ b/Utilities/cmcurl/lib/gopher.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,6 +28,7 @@ #include #include "transfer.h" #include "sendf.h" +#include "connect.h" #include "progress.h" #include "gopher.h" #include "select.h" @@ -83,8 +84,10 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) char *query = data->state.up.query; char *sel = NULL; char *sel_org = NULL; + timediff_t timeout_ms; ssize_t amount, k; size_t len; + int what; *done = TRUE; /* unconditionally */ @@ -113,7 +116,7 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) newp += 2; /* ... and finally unescape */ - result = Curl_urldecode(data, newp, 0, &sel, &len, FALSE); + result = Curl_urldecode(data, newp, 0, &sel, &len, REJECT_ZERO); free(gopherpath); if(result) return result; @@ -139,19 +142,29 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) else break; + timeout_ms = Curl_timeleft(conn->data, NULL, FALSE); + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } + if(!timeout_ms) + timeout_ms = TIMEDIFF_T_MAX; + /* Don't busyloop. The entire loop thing is a work-around as it causes a BLOCKING behavior which is a NO-NO. This function should rather be split up in a do and a doing piece where the pieces that aren't possible to send now will be sent in the doing function repeatedly until the entire request is sent. - - Wait a while for the socket to be writable. Note that this doesn't - acknowledge the timeout. */ - if(SOCKET_WRITABLE(sockfd, 100) < 0) { + what = SOCKET_WRITABLE(sockfd, timeout_ms); + if(what < 0) { result = CURLE_SEND_ERROR; break; } + else if(!what) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } } free(sel_org); diff --git a/Utilities/cmcurl/lib/gopher.h b/Utilities/cmcurl/lib/gopher.h index 501c990a85e..dec2557fc69 100644 --- a/Utilities/cmcurl/lib/gopher.h +++ b/Utilities/cmcurl/lib/gopher.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/hash.h b/Utilities/cmcurl/lib/hash.h index 90a25d1ca3f..558d0f47ca6 100644 --- a/Utilities/cmcurl/lib/hash.h +++ b/Utilities/cmcurl/lib/hash.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -80,7 +80,7 @@ int Curl_hash_delete(struct curl_hash *h, void *key, size_t key_len); void *Curl_hash_pick(struct curl_hash *, void *key, size_t key_len); void Curl_hash_apply(struct curl_hash *h, void *user, void (*cb)(void *user, void *ptr)); -int Curl_hash_count(struct curl_hash *h); +#define Curl_hash_count(h) ((h)->size) void Curl_hash_destroy(struct curl_hash *h); void Curl_hash_clean(struct curl_hash *h); void Curl_hash_clean_with_criterium(struct curl_hash *h, void *user, diff --git a/Utilities/cmcurl/lib/hmac.c b/Utilities/cmcurl/lib/hmac.c index bf49ebec54d..e4fea8a5079 100644 --- a/Utilities/cmcurl/lib/hmac.c +++ b/Utilities/cmcurl/lib/hmac.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,6 +30,7 @@ #include "curl_hmac.h" #include "curl_memory.h" +#include "warnless.h" /* The last #include file should be: */ #include "memdebug.h" @@ -47,13 +48,13 @@ static const unsigned char hmac_opad = 0x5C; -HMAC_context * -Curl_HMAC_init(const HMAC_params * hashparams, +struct HMAC_context * +Curl_HMAC_init(const struct HMAC_params *hashparams, const unsigned char *key, unsigned int keylen) { size_t i; - HMAC_context *ctxt; + struct HMAC_context *ctxt; unsigned char *hkey; unsigned char b; @@ -100,7 +101,7 @@ Curl_HMAC_init(const HMAC_params * hashparams, return ctxt; } -int Curl_HMAC_update(HMAC_context * ctxt, +int Curl_HMAC_update(struct HMAC_context *ctxt, const unsigned char *data, unsigned int len) { @@ -110,9 +111,9 @@ int Curl_HMAC_update(HMAC_context * ctxt, } -int Curl_HMAC_final(HMAC_context *ctxt, unsigned char *result) +int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *result) { - const HMAC_params * hashparams = ctxt->hmac_hash; + const struct HMAC_params *hashparams = ctxt->hmac_hash; /* Do not get result if called with a null parameter: only release storage. */ @@ -129,4 +130,41 @@ int Curl_HMAC_final(HMAC_context *ctxt, unsigned char *result) return 0; } +/* + * Curl_hmacit() + * + * This is used to generate a HMAC hash, for the specified input data, given + * the specified hash function and key. + * + * Parameters: + * + * hashparams [in] - The hash function (Curl_HMAC_MD5). + * key [in] - The key to use. + * keylen [in] - The length of the key. + * data [in] - The data to encrypt. + * datalen [in] - The length of the data. + * output [in/out] - The output buffer. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_hmacit(const struct HMAC_params *hashparams, + const unsigned char *key, const size_t keylen, + const unsigned char *data, const size_t datalen, + unsigned char *output) +{ + struct HMAC_context *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)); + + /* Finalise the digest */ + Curl_HMAC_final(ctxt, output); + + return CURLE_OK; +} + #endif /* CURL_DISABLE_CRYPTO_AUTH */ diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c index 99d872b3525..ed9190f4c6e 100644 --- a/Utilities/cmcurl/lib/hostasyn.c +++ b/Utilities/cmcurl/lib/hostasyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -117,10 +117,10 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn, * name resolve layers (selected at build-time). They all take this same set * of arguments */ -Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - const char *hostname, - int port, - int *waitp) +struct Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, + const char *hostname, + int port, + int *waitp) { return Curl_resolver_getaddrinfo(conn, hostname, port, waitp); } diff --git a/Utilities/cmcurl/lib/hostcheck.c b/Utilities/cmcurl/lib/hostcheck.c index 115d24b2e2f..9e0db05fac8 100644 --- a/Utilities/cmcurl/lib/hostcheck.c +++ b/Utilities/cmcurl/lib/hostcheck.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/hostcheck.h b/Utilities/cmcurl/lib/hostcheck.h index f562df9ae75..9c180856ada 100644 --- a/Utilities/cmcurl/lib/hostcheck.h +++ b/Utilities/cmcurl/lib/hostcheck.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index cf33ed8f4dd..dd5916e338e 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -59,6 +59,7 @@ #include "strerror.h" #include "url.h" #include "inet_ntop.h" +#include "inet_pton.h" #include "multiif.h" #include "doh.h" #include "warnless.h" @@ -119,7 +120,7 @@ static void freednsentry(void *freethis); /* * Return # of addresses in a Curl_addrinfo struct */ -int Curl_num_addresses(const Curl_addrinfo *addr) +int Curl_num_addresses(const struct Curl_addrinfo *addr) { int i = 0; while(addr) { @@ -130,39 +131,36 @@ int Curl_num_addresses(const Curl_addrinfo *addr) } /* - * Curl_printable_address() returns a printable version of the 1st address + * 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 * bufsize bytes big. * - * If the conversion fails, it returns NULL. + * If the conversion fails, the target buffer is empty. */ -const char * -Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize) +void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, + size_t bufsize) { - const struct sockaddr_in *sa4; - const struct in_addr *ipaddr4; -#ifdef ENABLE_IPV6 - const struct sockaddr_in6 *sa6; - const struct in6_addr *ipaddr6; -#endif + DEBUGASSERT(bufsize); + buf[0] = 0; switch(ai->ai_family) { - case AF_INET: - sa4 = (const void *)ai->ai_addr; - ipaddr4 = &sa4->sin_addr; - return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, - bufsize); + case AF_INET: { + const struct sockaddr_in *sa4 = (const void *)ai->ai_addr; + const struct in_addr *ipaddr4 = &sa4->sin_addr; + (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize); + break; + } #ifdef ENABLE_IPV6 - case AF_INET6: - sa6 = (const void *)ai->ai_addr; - ipaddr6 = &sa6->sin6_addr; - return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, - bufsize); + case AF_INET6: { + const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr; + const struct in6_addr *ipaddr6 = &sa6->sin6_addr; + (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize); + break; + } #endif - default: - break; + default: + break; } - return NULL; } /* @@ -336,7 +334,7 @@ Curl_fetch_addr(struct connectdata *conn, #ifndef CURL_DISABLE_SHUFFLE_DNS UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, - Curl_addrinfo **addr); + struct Curl_addrinfo **addr); /* * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' * struct by re-linking its linked list. @@ -350,13 +348,13 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, * @unittest: 1608 */ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, - Curl_addrinfo **addr) + struct Curl_addrinfo **addr) { CURLcode result = CURLE_OK; const int num_addrs = Curl_num_addresses(*addr); if(num_addrs > 1) { - Curl_addrinfo **nodes; + struct Curl_addrinfo **nodes; infof(data, "Shuffling %i addresses", num_addrs); nodes = malloc(num_addrs*sizeof(*nodes)); @@ -375,7 +373,7 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, if(rnd) { /* Fisher-Yates shuffle */ if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { - Curl_addrinfo *swap_tmp; + struct Curl_addrinfo *swap_tmp; for(i = num_addrs - 1; i > 0; i--) { swap_tmp = nodes[rnd[i] % (i + 1)]; nodes[rnd[i] % (i + 1)] = nodes[i]; @@ -414,7 +412,7 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, */ struct Curl_dns_entry * Curl_cache_addr(struct Curl_easy *data, - Curl_addrinfo *addr, + struct Curl_addrinfo *addr, const char *hostname, int port) { @@ -482,18 +480,19 @@ Curl_cache_addr(struct Curl_easy *data, * CURLRESOLV_PENDING (1) = waiting for response, no pointer */ -int Curl_resolv(struct connectdata *conn, - const char *hostname, - int port, - bool allowDOH, - struct Curl_dns_entry **entry) +enum resolve_t Curl_resolv(struct connectdata *conn, + const char *hostname, + int port, + bool allowDOH, + struct Curl_dns_entry **entry) { struct Curl_dns_entry *dns = NULL; struct Curl_easy *data = conn->data; CURLcode result; - int rc = CURLRESOLV_ERROR; /* default to failure */ + enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */ *entry = NULL; + conn->bits.doh = FALSE; /* default is not */ if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); @@ -512,13 +511,13 @@ int Curl_resolv(struct connectdata *conn, if(!dns) { /* The entry was not in the cache. Resolve it to IP address */ - Curl_addrinfo *addr; + struct Curl_addrinfo *addr = NULL; int respwait = 0; - - /* Check what IP specifics the app has requested and if we can provide it. - * If not, bail out. */ - if(!Curl_ipvalid(conn)) - return CURLRESOLV_ERROR; + struct in_addr in; +#ifndef USE_RESOLVE_ON_IPS + const +#endif + bool ipnum = FALSE; /* notify the resolver start callback */ if(data->set.resolver_start) { @@ -531,20 +530,59 @@ int Curl_resolv(struct connectdata *conn, return CURLRESOLV_ERROR; } - if(allowDOH && data->set.doh) { - addr = Curl_doh(conn, hostname, port, &respwait); +#ifndef USE_RESOLVE_ON_IPS + /* First check if this is an IPv4 address string */ + if(Curl_inet_pton(AF_INET, hostname, &in) > 0) + /* This is a dotted IP address 123.123.123.123-style */ + addr = Curl_ip2addr(AF_INET, &in, hostname, port); +#ifdef ENABLE_IPV6 + if(!addr) { + struct in6_addr in6; + /* check if this is an IPv6 address string */ + if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) + /* This is an IPv6 address literal */ + addr = Curl_ip2addr(AF_INET6, &in6, hostname, port); } +#endif /* ENABLE_IPV6 */ + +#else /* if USE_RESOLVE_ON_IPS */ + /* First check if this is an IPv4 address string */ + if(Curl_inet_pton(AF_INET, hostname, &in) > 0) + /* This is a dotted IP address 123.123.123.123-style */ + ipnum = TRUE; +#ifdef ENABLE_IPV6 else { - /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a - non-zero value indicating that we need to wait for the response to the - resolve call */ - addr = Curl_getaddrinfo(conn, + struct in6_addr in6; + /* check if this is an IPv6 address string */ + if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) + /* This is an IPv6 address literal */ + ipnum = TRUE; + } +#endif /* ENABLE_IPV6 */ + +#endif /* !USE_RESOLVE_ON_IPS */ + + if(!addr) { + /* Check what IP specifics the app has requested and if we can provide + * it. If not, bail out. */ + if(!Curl_ipvalid(conn)) + return CURLRESOLV_ERROR; + + if(allowDOH && data->set.doh && !ipnum) { + addr = Curl_doh(conn, hostname, port, &respwait); + } + else { + /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a + non-zero value indicating that we need to wait for the response to + the resolve call */ + addr = Curl_getaddrinfo(conn, #ifdef DEBUGBUILD - (data->set.str[STRING_DEVICE] - && !strcmp(data->set.str[STRING_DEVICE], - "LocalHost"))?"localhost": + (data->set.str[STRING_DEVICE] + && !strcmp(data->set.str[STRING_DEVICE], + "LocalHost"))?"localhost": #endif - hostname, port, &respwait); + hostname, port, &respwait); + } } if(!addr) { if(respwait) { @@ -620,11 +658,11 @@ RETSIGTYPE alarmfunc(int sig) * CURLRESOLV_PENDING (1) = waiting for response, no pointer */ -int Curl_resolv_timeout(struct connectdata *conn, - const char *hostname, - int port, - struct Curl_dns_entry **entry, - time_t timeoutms) +enum resolve_t Curl_resolv_timeout(struct connectdata *conn, + const char *hostname, + int port, + struct Curl_dns_entry **entry, + timediff_t timeoutms) { #ifdef USE_ALARM_TIMEOUT #ifdef HAVE_SIGACTION @@ -640,7 +678,7 @@ int Curl_resolv_timeout(struct connectdata *conn, volatile unsigned int prev_alarm = 0; struct Curl_easy *data = conn->data; #endif /* USE_ALARM_TIMEOUT */ - int rc; + enum resolve_t rc; *entry = NULL; @@ -749,7 +787,7 @@ int Curl_resolv_timeout(struct connectdata *conn, conn->created) / 1000; /* the alarm period is counted in even number of seconds */ - unsigned long alarm_set = prev_alarm - elapsed_secs; + unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs); if(!alarm_set || ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) { @@ -868,7 +906,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) } else { struct Curl_dns_entry *dns; - Curl_addrinfo *head = NULL, *tail = NULL; + struct Curl_addrinfo *head = NULL, *tail = NULL; size_t entry_len; char address[64]; #if !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -902,7 +940,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) while(*end_ptr) { size_t alen; - Curl_addrinfo *ai; + struct Curl_addrinfo *ai; addr_begin = end_ptr + 1; addr_end = strchr(addr_begin, ','); @@ -1021,25 +1059,27 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) CURLcode Curl_resolv_check(struct connectdata *conn, struct Curl_dns_entry **dns) { - if(conn->data->set.doh) +#if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH) + (void)dns; +#endif + + if(conn->bits.doh) return Curl_doh_is_resolved(conn, dns); return Curl_resolver_is_resolved(conn, dns); } int Curl_resolv_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { #ifdef CURLRES_ASYNCH - if(conn->data->set.doh) + if(conn->bits.doh) /* nothing to wait for during DOH resolve, those handles have their own sockets */ return GETSOCK_BLANK; - return Curl_resolver_getsock(conn, socks, numsocks); + return Curl_resolver_getsock(conn, socks); #else (void)conn; (void)socks; - (void)numsocks; return GETSOCK_BLANK; #endif } @@ -1061,10 +1101,12 @@ CURLcode Curl_once_resolved(struct connectdata *conn, result = Curl_setup_conn(conn, protocol_done); - if(result) - /* We're not allowed to return failure with memory left allocated - in the connectdata struct, free those here */ - Curl_disconnect(conn->data, conn, TRUE); /* close the connection */ - + if(result) { + struct Curl_easy *data = conn->data; + DEBUGASSERT(data); + Curl_detach_connnection(data); + Curl_conncache_remove_conn(data, conn, TRUE); + Curl_disconnect(data, conn, TRUE); + } return result; } diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index 9dc0d5a1776..374b06c8555 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,6 +25,7 @@ #include "curl_setup.h" #include "hash.h" #include "curl_addrinfo.h" +#include "timeval.h" /* for timediff_t */ #include "asyn.h" #ifdef HAVE_SETJMP_H @@ -61,10 +62,9 @@ struct connectdata; * Returns a struct curl_hash pointer on success, NULL on failure. */ struct curl_hash *Curl_global_host_cache_init(void); -void Curl_global_host_cache_dtor(void); struct Curl_dns_entry { - Curl_addrinfo *addr; + struct Curl_addrinfo *addr; /* timestamp == 0 -- CURLOPT_RESOLVE entry, doesn't timeout */ time_t timestamp; /* use-counter, use Curl_resolv_unlock to release reference */ @@ -79,26 +79,29 @@ struct Curl_dns_entry { * use, or we'll leak memory! */ /* return codes */ -#define CURLRESOLV_TIMEDOUT -2 -#define CURLRESOLV_ERROR -1 -#define CURLRESOLV_RESOLVED 0 -#define CURLRESOLV_PENDING 1 -int Curl_resolv(struct connectdata *conn, - const char *hostname, - int port, - bool allowDOH, - struct Curl_dns_entry **dnsentry); -int Curl_resolv_timeout(struct connectdata *conn, const char *hostname, - int port, struct Curl_dns_entry **dnsentry, - time_t timeoutms); +enum resolve_t { + CURLRESOLV_TIMEDOUT = -2, + CURLRESOLV_ERROR = -1, + CURLRESOLV_RESOLVED = 0, + CURLRESOLV_PENDING = 1 +}; +enum resolve_t Curl_resolv(struct connectdata *conn, + const char *hostname, + int port, + bool allowDOH, + struct Curl_dns_entry **dnsentry); +enum resolve_t Curl_resolv_timeout(struct connectdata *conn, + const char *hostname, int port, + struct Curl_dns_entry **dnsentry, + timediff_t timeoutms); #ifdef CURLRES_IPV6 /* * Curl_ipv6works() returns TRUE if IPv6 seems to work. */ -bool Curl_ipv6works(void); +bool Curl_ipv6works(struct connectdata *conn); #else -#define Curl_ipv6works() FALSE +#define Curl_ipv6works(x) FALSE #endif /* @@ -114,19 +117,16 @@ bool Curl_ipvalid(struct connectdata *conn); * name resolve layers (selected at build-time). They all take this same set * of arguments */ -Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - const char *hostname, - int port, - int *waitp); +struct Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, + const char *hostname, + int port, + int *waitp); /* unlock a previously resolved dns entry */ void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns); -/* for debugging purposes only: */ -void Curl_scan_cache_used(void *user, void *ptr); - /* init a new dns cache and return success */ int Curl_mk_dnscache(struct curl_hash *hash); @@ -134,7 +134,7 @@ int Curl_mk_dnscache(struct curl_hash *hash); void Curl_hostcache_prune(struct Curl_easy *data); /* Return # of addresses in a Curl_addrinfo struct */ -int Curl_num_addresses(const Curl_addrinfo *addr); +int Curl_num_addresses(const struct Curl_addrinfo *addr); #if defined(CURLDEBUG) && defined(HAVE_GETNAMEINFO) int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa, @@ -146,7 +146,7 @@ int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa, #endif /* IPv4 threadsafe resolve function used for synch and asynch builds */ -Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port); +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port); CURLcode Curl_once_resolved(struct connectdata *conn, bool *protocol_connect); @@ -158,15 +158,15 @@ CURLcode Curl_once_resolved(struct connectdata *conn, bool *protocol_connect); */ CURLcode Curl_addrinfo_callback(struct connectdata *conn, int status, - Curl_addrinfo *ai); + struct Curl_addrinfo *ai); /* * 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 * bufsize bytes big. */ -const char *Curl_printable_address(const Curl_addrinfo *ip, - char *buf, size_t bufsize); +void Curl_printable_address(const struct Curl_addrinfo *ip, + char *buf, size_t bufsize); /* * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. @@ -187,7 +187,7 @@ Curl_fetch_addr(struct connectdata *conn, * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. */ struct Curl_dns_entry * -Curl_cache_addr(struct Curl_easy *data, Curl_addrinfo *addr, +Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, const char *hostname, int port); #ifndef INADDR_NONE @@ -236,11 +236,6 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, */ void Curl_hostcache_clean(struct Curl_easy *data, struct curl_hash *hash); -/* - * Destroy the hostcache of this handle. - */ -void Curl_hostcache_destroy(struct Curl_easy *data); - /* * Populate the cache with specified entries from CURLOPT_RESOLVE. */ @@ -249,7 +244,6 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data); CURLcode Curl_resolv_check(struct connectdata *conn, struct Curl_dns_entry **dns); int Curl_resolv_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks); + curl_socket_t *socks); #endif /* HEADER_CURL_HOSTIP_H */ diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c index e6ba710d830..eae9416741b 100644 --- a/Utilities/cmcurl/lib/hostip4.c +++ b/Utilities/cmcurl/lib/hostip4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -52,7 +52,6 @@ #include "share.h" #include "strerror.h" #include "url.h" -#include "inet_pton.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -89,12 +88,12 @@ bool Curl_ipvalid(struct connectdata *conn) * flavours have thread-safe versions of the plain gethostbyname() etc. * */ -Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - const char *hostname, - int port, - int *waitp) +struct Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, + const char *hostname, + int port, + int *waitp) { - Curl_addrinfo *ai = NULL; + struct Curl_addrinfo *ai = NULL; #ifdef CURL_DISABLE_VERBOSE_STRINGS (void)conn; @@ -120,36 +119,30 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, * implying that only threadsafe code and function calls may be used. * */ -Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, - int port) +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, + int port) { #if !defined(HAVE_GETADDRINFO_THREADSAFE) && defined(HAVE_GETHOSTBYNAME_R_3) int res; #endif - Curl_addrinfo *ai = NULL; + struct Curl_addrinfo *ai = NULL; struct hostent *h = NULL; - struct in_addr in; struct hostent *buf = NULL; - if(Curl_inet_pton(AF_INET, hostname, &in) > 0) - /* This is a dotted IP address 123.123.123.123-style */ - return Curl_ip2addr(AF_INET, &in, hostname, port); - #if defined(HAVE_GETADDRINFO_THREADSAFE) - else { - struct addrinfo hints; - char sbuf[12]; - char *sbufptr = NULL; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_INET; - hints.ai_socktype = SOCK_STREAM; - if(port) { - msnprintf(sbuf, sizeof(sbuf), "%d", port); - sbufptr = sbuf; - } + struct addrinfo hints; + char sbuf[12]; + char *sbufptr = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + if(port) { + msnprintf(sbuf, sizeof(sbuf), "%d", port); + sbufptr = sbuf; + } - (void)Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &ai); + (void)Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &ai); #elif defined(HAVE_GETHOSTBYNAME_R) /* @@ -157,144 +150,141 @@ Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, * Since there are three different versions of it, the following code is * somewhat #ifdef-ridden. */ - else { - int h_errnop; - - buf = calloc(1, CURL_HOSTENT_SIZE); - if(!buf) - return NULL; /* major failure */ - /* - * The clearing of the buffer is a workaround for a gethostbyname_r bug in - * qnx nto and it is also _required_ for some of these functions on some - * platforms. - */ + int h_errnop; + + buf = calloc(1, CURL_HOSTENT_SIZE); + if(!buf) + return NULL; /* major failure */ + /* + * The clearing of the buffer is a workaround for a gethostbyname_r bug in + * qnx nto and it is also _required_ for some of these functions on some + * platforms. + */ #if defined(HAVE_GETHOSTBYNAME_R_5) - /* Solaris, IRIX and more */ - h = gethostbyname_r(hostname, - (struct hostent *)buf, - (char *)buf + sizeof(struct hostent), - CURL_HOSTENT_SIZE - sizeof(struct hostent), - &h_errnop); - - /* If the buffer is too small, it returns NULL and sets errno to - * ERANGE. The errno is thread safe if this is compiled with - * -D_REENTRANT as then the 'errno' variable is a macro defined to get - * used properly for threads. - */ + /* Solaris, IRIX and more */ + h = gethostbyname_r(hostname, + (struct hostent *)buf, + (char *)buf + sizeof(struct hostent), + CURL_HOSTENT_SIZE - sizeof(struct hostent), + &h_errnop); + + /* If the buffer is too small, it returns NULL and sets errno to + * ERANGE. The errno is thread safe if this is compiled with + * -D_REENTRANT as then the 'errno' variable is a macro defined to get + * used properly for threads. + */ - if(h) { - ; - } - else + if(h) { + ; + } + else #elif defined(HAVE_GETHOSTBYNAME_R_6) - /* Linux */ - - (void)gethostbyname_r(hostname, - (struct hostent *)buf, - (char *)buf + sizeof(struct hostent), - CURL_HOSTENT_SIZE - sizeof(struct hostent), - &h, /* DIFFERENCE */ - &h_errnop); - /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a - * sudden this function returns EAGAIN if the given buffer size is too - * small. Previous versions are known to return ERANGE for the same - * problem. - * - * This wouldn't be such a big problem if older versions wouldn't - * sometimes return EAGAIN on a common failure case. Alas, we can't - * assume that EAGAIN *or* ERANGE means ERANGE for any given version of - * glibc. - * - * For now, we do that and thus we may call the function repeatedly and - * fail for older glibc versions that return EAGAIN, until we run out of - * buffer size (step_size grows beyond CURL_HOSTENT_SIZE). - * - * If anyone has a better fix, please tell us! - * - * ------------------------------------------------------------------- - * - * On October 23rd 2003, Dan C dug up more details on the mysteries of - * gethostbyname_r() in glibc: - * - * In glibc 2.2.5 the interface is different (this has also been - * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't - * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32 - * (shipped/upgraded by Redhat 7.2) don't show this behavior! - * - * In this "buggy" version, the return code is -1 on error and 'errno' - * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a - * thread-safe variable. - */ + /* Linux */ + + (void)gethostbyname_r(hostname, + (struct hostent *)buf, + (char *)buf + sizeof(struct hostent), + CURL_HOSTENT_SIZE - sizeof(struct hostent), + &h, /* DIFFERENCE */ + &h_errnop); + /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a + * sudden this function returns EAGAIN if the given buffer size is too + * small. Previous versions are known to return ERANGE for the same + * problem. + * + * This wouldn't be such a big problem if older versions wouldn't + * sometimes return EAGAIN on a common failure case. Alas, we can't + * assume that EAGAIN *or* ERANGE means ERANGE for any given version of + * glibc. + * + * For now, we do that and thus we may call the function repeatedly and + * fail for older glibc versions that return EAGAIN, until we run out of + * buffer size (step_size grows beyond CURL_HOSTENT_SIZE). + * + * If anyone has a better fix, please tell us! + * + * ------------------------------------------------------------------- + * + * On October 23rd 2003, Dan C dug up more details on the mysteries of + * gethostbyname_r() in glibc: + * + * In glibc 2.2.5 the interface is different (this has also been + * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't + * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32 + * (shipped/upgraded by Redhat 7.2) don't show this behavior! + * + * In this "buggy" version, the return code is -1 on error and 'errno' + * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a + * thread-safe variable. + */ - if(!h) /* failure */ + if(!h) /* failure */ #elif defined(HAVE_GETHOSTBYNAME_R_3) - /* AIX, Digital Unix/Tru64, HPUX 10, more? */ - - /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of - * the plain fact that it does not return unique full buffers on each - * call, but instead several of the pointers in the hostent structs will - * point to the same actual data! This have the unfortunate down-side that - * our caching system breaks down horribly. Luckily for us though, AIX 4.3 - * and more recent versions have a "completely thread-safe"[*] libc where - * all the data is stored in thread-specific memory areas making calls to - * the plain old gethostbyname() work fine even for multi-threaded - * programs. - * - * This AIX 4.3 or later detection is all made in the configure script. - * - * Troels Walsted Hansen helped us work this out on March 3rd, 2003. - * - * [*] = much later we've found out that it isn't at all "completely - * thread-safe", but at least the gethostbyname() function is. + /* AIX, Digital Unix/Tru64, HPUX 10, more? */ + + /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of + * the plain fact that it does not return unique full buffers on each + * call, but instead several of the pointers in the hostent structs will + * point to the same actual data! This have the unfortunate down-side that + * our caching system breaks down horribly. Luckily for us though, AIX 4.3 + * and more recent versions have a "completely thread-safe"[*] libc where + * all the data is stored in thread-specific memory areas making calls to + * the plain old gethostbyname() work fine even for multi-threaded + * programs. + * + * This AIX 4.3 or later detection is all made in the configure script. + * + * Troels Walsted Hansen helped us work this out on March 3rd, 2003. + * + * [*] = much later we've found out that it isn't at all "completely + * thread-safe", but at least the gethostbyname() function is. + */ + + if(CURL_HOSTENT_SIZE >= + (sizeof(struct hostent) + sizeof(struct hostent_data))) { + + /* August 22nd, 2000: Albert Chin-A-Young brought an updated version + * that should work! September 20: Richard Prescott worked on the buffer + * size dilemma. */ - if(CURL_HOSTENT_SIZE >= - (sizeof(struct hostent) + sizeof(struct hostent_data))) { - - /* August 22nd, 2000: Albert Chin-A-Young brought an updated version - * that should work! September 20: Richard Prescott worked on the buffer - * size dilemma. - */ - - res = gethostbyname_r(hostname, - (struct hostent *)buf, - (struct hostent_data *)((char *)buf + - sizeof(struct hostent))); - h_errnop = SOCKERRNO; /* we don't deal with this, but set it anyway */ - } - else - res = -1; /* failure, too smallish buffer size */ - - if(!res) { /* success */ - - h = buf; /* result expected in h */ - - /* This is the worst kind of the different gethostbyname_r() interfaces. - * Since we don't know how big buffer this particular lookup required, - * we can't realloc down the huge alloc without doing closer analysis of - * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every - * name lookup. Fixing this would require an extra malloc() and then - * calling Curl_addrinfo_copy() that subsequent realloc()s down the new - * memory area to the actually used amount. - */ - } - else + res = gethostbyname_r(hostname, + (struct hostent *)buf, + (struct hostent_data *)((char *)buf + + sizeof(struct hostent))); + h_errnop = SOCKERRNO; /* we don't deal with this, but set it anyway */ + } + else + res = -1; /* failure, too smallish buffer size */ + + if(!res) { /* success */ + + h = buf; /* result expected in h */ + + /* This is the worst kind of the different gethostbyname_r() interfaces. + * Since we don't know how big buffer this particular lookup required, + * we can't realloc down the huge alloc without doing closer analysis of + * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every + * name lookup. Fixing this would require an extra malloc() and then + * calling Curl_addrinfo_copy() that subsequent realloc()s down the new + * memory area to the actually used amount. + */ + } + else #endif /* HAVE_...BYNAME_R_5 || HAVE_...BYNAME_R_6 || HAVE_...BYNAME_R_3 */ - { - h = NULL; /* set return code to NULL */ - free(buf); - } + { + h = NULL; /* set return code to NULL */ + free(buf); + } #else /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */ - /* - * Here is code for platforms that don't have a thread safe - * getaddrinfo() nor gethostbyname_r() function or for which - * gethostbyname() is the preferred one. - */ - else { - h = gethostbyname((void *)hostname); + /* + * Here is code for platforms that don't have a thread safe + * getaddrinfo() nor gethostbyname_r() function or for which + * gethostbyname() is the preferred one. + */ + h = gethostbyname((void *)hostname); #endif /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */ - } if(h) { ai = Curl_he2ai(h, port); diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c index 5511f1aab14..11215758dd3 100644 --- a/Utilities/cmcurl/lib/hostip6.c +++ b/Utilities/cmcurl/lib/hostip6.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -62,13 +62,19 @@ /* * Curl_ipv6works() returns TRUE if IPv6 seems to work. */ -bool Curl_ipv6works(void) +bool Curl_ipv6works(struct connectdata *conn) { - /* the nature of most system is that IPv6 status doesn't come and go - during a program's lifetime so we only probe the first time and then we - have the info kept for fast re-use */ - static int ipv6_works = -1; - if(-1 == ipv6_works) { + if(conn) { + /* the nature of most system is that IPv6 status doesn't come and go + during a program's lifetime so we only probe the first time and then we + have the info kept for fast re-use */ + DEBUGASSERT(conn); + DEBUGASSERT(conn->data); + DEBUGASSERT(conn->data->multi); + return conn->data->multi->ipv6_works; + } + else { + int ipv6_works = -1; /* probe to see if we have a working IPv6 stack */ curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0); if(s == CURL_SOCKET_BAD) @@ -78,8 +84,8 @@ bool Curl_ipv6works(void) ipv6_works = 1; Curl_closesocket(NULL, s); } + return (ipv6_works>0)?TRUE:FALSE; } - return (ipv6_works>0)?TRUE:FALSE; } /* @@ -89,7 +95,7 @@ bool Curl_ipv6works(void) bool Curl_ipvalid(struct connectdata *conn) { if(conn->ip_version == CURL_IPRESOLVE_V6) - return Curl_ipv6works(); + return Curl_ipv6works(conn); return TRUE; } @@ -97,20 +103,16 @@ bool Curl_ipvalid(struct connectdata *conn) #if defined(CURLRES_SYNCH) #ifdef DEBUG_ADDRINFO -static void dump_addrinfo(struct connectdata *conn, const Curl_addrinfo *ai) +static void dump_addrinfo(struct connectdata *conn, + const struct Curl_addrinfo *ai) { printf("dump_addrinfo:\n"); for(; ai; ai = ai->ai_next) { char buf[INET6_ADDRSTRLEN]; printf(" fam %2d, CNAME %s, ", ai->ai_family, ai->ai_canonname ? ai->ai_canonname : ""); - if(Curl_printable_address(ai, buf, sizeof(buf))) - printf("%s\n", buf); - else { - char buffer[STRERROR_LEN]; - printf("failed; %s\n", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - } + Curl_printable_address(ai, buf, sizeof(buf)); + printf("%s\n", buf); } } #else @@ -126,13 +128,13 @@ static void dump_addrinfo(struct connectdata *conn, const Curl_addrinfo *ai) * memory we need to free after use. That memory *MUST* be freed with * Curl_freeaddrinfo(), nothing else. */ -Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, - const char *hostname, - int port, - int *waitp) +struct Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, + const char *hostname, + int port, + int *waitp) { struct addrinfo hints; - Curl_addrinfo *res; + struct Curl_addrinfo *res; int error; char sbuf[12]; char *sbufptr = NULL; @@ -159,13 +161,14 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, break; } - if((pf != PF_INET) && !Curl_ipv6works()) + if((pf != PF_INET) && !Curl_ipv6works(conn)) /* The stack seems to be a non-IPv6 one */ pf = PF_INET; memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = conn->socktype; + hints.ai_socktype = (conn->transport == TRNSPRT_TCP) ? + SOCK_STREAM : SOCK_DGRAM; #ifndef USE_RESOLVE_ON_IPS /* diff --git a/Utilities/cmcurl/lib/hostsyn.c b/Utilities/cmcurl/lib/hostsyn.c index 3de6746f523..9e31008d2c9 100644 --- a/Utilities/cmcurl/lib/hostsyn.c +++ b/Utilities/cmcurl/lib/hostsyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 338c59a22cc..28d66c287f3 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -88,8 +88,7 @@ */ static int http_getsock_do(struct connectdata *conn, - curl_socket_t *socks, - int numsocks); + curl_socket_t *socks); static int http_should_fail(struct connectdata *conn); #ifndef CURL_DISABLE_PROXY @@ -99,8 +98,7 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn); #ifdef USE_SSL static CURLcode https_connecting(struct connectdata *conn, bool *done); static int https_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks); + curl_socket_t *socks); #else #define https_connecting(x,y) CURLE_COULDNT_CONNECT #endif @@ -127,7 +125,8 @@ const struct Curl_handler Curl_handler_http = { ZERO_NULL, /* connection_check */ PORT_HTTP, /* defport */ CURLPROTO_HTTP, /* protocol */ - PROTOPT_CREDSPERREQUEST /* flags */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL }; #ifdef USE_SSL @@ -152,7 +151,8 @@ const struct Curl_handler Curl_handler_https = { ZERO_NULL, /* connection_check */ PORT_HTTPS, /* defport */ CURLPROTO_HTTPS, /* protocol */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN_NPN /* flags */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN_NPN | /* flags */ + PROTOPT_USERPWDCTRL }; #endif @@ -171,10 +171,22 @@ static CURLcode http_setup_conn(struct connectdata *conn) Curl_mime_initpart(&http->form, conn->data); data->req.protop = http; - if(!CONN_INUSE(conn)) - /* if not already multi-using, setup connection details */ - Curl_http2_setup_conn(conn); - Curl_http2_setup_req(data); + if(data->set.httpversion == CURL_HTTP_VERSION_3) { + if(conn->handler->flags & PROTOPT_SSL) + /* Only go HTTP/3 directly on HTTPS URLs. It needs a UDP socket and does + the QUIC dance. */ + conn->transport = TRNSPRT_QUIC; + else { + failf(data, "HTTP/3 requested for non-HTTPS URL"); + return CURLE_URL_MALFORMAT; + } + } + else { + if(!CONN_INUSE(conn)) + /* if not already multi-using, setup connection details */ + Curl_http2_setup_conn(conn); + Curl_http2_setup_req(data); + } return CURLE_OK; } @@ -258,7 +270,7 @@ char *Curl_copy_header_value(const char *header) return NULL; memcpy(value, start, len); - value[len] = 0; /* zero terminate */ + value[len] = 0; /* null-terminate */ return value; } @@ -282,17 +294,21 @@ static CURLcode http_output_basic(struct connectdata *conn, bool proxy) char *out; if(proxy) { - userp = &conn->allocptr.proxyuserpwd; +#ifndef CURL_DISABLE_PROXY + userp = &data->state.aptr.proxyuserpwd; user = conn->http_proxy.user; pwd = conn->http_proxy.passwd; +#else + return CURLE_NOT_BUILT_IN; +#endif } else { - userp = &conn->allocptr.userpwd; + userp = &data->state.aptr.userpwd; user = conn->user; pwd = conn->passwd; } - out = aprintf("%s:%s", user, pwd); + out = aprintf("%s:%s", user, pwd ? pwd : ""); if(!out) return CURLE_OUT_OF_MEMORY; @@ -330,11 +346,12 @@ static CURLcode http_output_bearer(struct connectdata *conn) { char **userp; CURLcode result = CURLE_OK; + struct Curl_easy *data = conn->data; - userp = &conn->allocptr.userpwd; + userp = &data->state.aptr.userpwd; free(*userp); *userp = aprintf("Authorization: Bearer %s\r\n", - conn->oauth_bearer); + conn->data->set.str[STRING_BEARER]); if(!*userp) { result = CURLE_OUT_OF_MEMORY; @@ -383,7 +400,7 @@ static bool pickoneauth(struct auth *pick, unsigned long mask) } /* - * Curl_http_perhapsrewind() + * http_perhapsrewind() * * If we are doing POST or PUT { * If we have more data to send { @@ -417,7 +434,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) skip this rewinding stuff */ return CURLE_OK; - switch(data->set.httpreq) { + switch(data->state.httpreq) { case HTTPREQ_GET: case HTTPREQ_HEAD: return CURLE_OK; @@ -438,11 +455,8 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) } else { /* figure out how much data we are expected to send */ - switch(data->set.httpreq) { + switch(data->state.httpreq) { case HTTPREQ_POST: - if(data->state.infilesize != -1) - expectsend = data->state.infilesize; - break; case HTTPREQ_PUT: if(data->state.infilesize != -1) expectsend = data->state.infilesize; @@ -548,7 +562,7 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) CURLcode result = CURLE_OK; unsigned long authmask = ~0ul; - if(!conn->oauth_bearer) + if(!data->set.str[STRING_BEARER]) authmask &= (unsigned long)~CURLAUTH_BEARER; if(100 <= data->req.httpcode && 199 >= data->req.httpcode) @@ -558,7 +572,7 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) if(data->state.authproblem) return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK; - if((conn->bits.user_passwd || conn->oauth_bearer) && + if((conn->bits.user_passwd || data->set.str[STRING_BEARER]) && ((data->req.httpcode == 401) || (conn->bits.authneg && data->req.httpcode < 300))) { pickhost = pickoneauth(&data->state.authhost, authmask); @@ -571,6 +585,7 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) conn->data->set.httpversion = CURL_HTTP_VERSION_1_1; } } +#ifndef CURL_DISABLE_PROXY if(conn->bits.proxy_user_passwd && ((data->req.httpcode == 407) || (conn->bits.authneg && data->req.httpcode < 300))) { @@ -579,10 +594,11 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) if(!pickproxy) data->state.authproblem = TRUE; } +#endif if(pickhost || pickproxy) { - if((data->set.httpreq != HTTPREQ_GET) && - (data->set.httpreq != HTTPREQ_HEAD) && + if((data->state.httpreq != HTTPREQ_GET) && + (data->state.httpreq != HTTPREQ_HEAD) && !conn->bits.rewindaftersend) { result = http_perhapsrewind(conn); if(result) @@ -603,8 +619,8 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) authentication is not "done" yet and no authentication seems to be required and we didn't try HEAD or GET */ - if((data->set.httpreq != HTTPREQ_GET) && - (data->set.httpreq != HTTPREQ_HEAD)) { + if((data->state.httpreq != HTTPREQ_GET) && + (data->state.httpreq != HTTPREQ_HEAD)) { data->req.newurl = strdup(data->change.url); /* clone URL */ if(!data->req.newurl) return CURLE_OUT_OF_MEMORY; @@ -634,9 +650,7 @@ output_auth_headers(struct connectdata *conn, { const char *auth = NULL; CURLcode result = CURLE_OK; -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(USE_SPNEGO) struct Curl_easy *data = conn->data; -#endif #ifdef CURL_DISABLE_CRYPTO_AUTH (void)request; @@ -644,7 +658,7 @@ output_auth_headers(struct connectdata *conn, #endif #ifdef USE_SPNEGO - if((authstatus->picked == CURLAUTH_NEGOTIATE)) { + if(authstatus->picked == CURLAUTH_NEGOTIATE) { auth = "Negotiate"; result = Curl_output_negotiate(conn, proxy); if(result) @@ -684,10 +698,13 @@ output_auth_headers(struct connectdata *conn, #endif if(authstatus->picked == CURLAUTH_BASIC) { /* Basic */ - if((proxy && conn->bits.proxy_user_passwd && - !Curl_checkProxyheaders(conn, "Proxy-authorization")) || - (!proxy && conn->bits.user_passwd && - !Curl_checkheaders(conn, "Authorization"))) { + if( +#ifndef CURL_DISABLE_PROXY + (proxy && conn->bits.proxy_user_passwd && + !Curl_checkProxyheaders(conn, "Proxy-authorization")) || +#endif + (!proxy && conn->bits.user_passwd && + !Curl_checkheaders(conn, "Authorization"))) { auth = "Basic"; result = http_output_basic(conn, proxy); if(result) @@ -700,7 +717,7 @@ output_auth_headers(struct connectdata *conn, } if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ - if((!proxy && conn->oauth_bearer && + if((!proxy && data->set.str[STRING_BEARER] && !Curl_checkheaders(conn, "Authorization:"))) { auth = "Bearer"; result = http_output_bearer(conn); @@ -714,10 +731,15 @@ output_auth_headers(struct connectdata *conn, } if(auth) { +#ifndef CURL_DISABLE_PROXY infof(data, "%s auth using %s with user '%s'\n", proxy ? "Proxy" : "Server", auth, proxy ? (conn->http_proxy.user ? conn->http_proxy.user : "") : - (conn->user ? conn->user : "")); + (conn->user ? conn->user : "")); +#else + infof(data, "Server auth using %s with user '%s'\n", + auth, conn->user ? conn->user : ""); +#endif authstatus->multipass = (!authstatus->done) ? TRUE : FALSE; } else @@ -757,8 +779,11 @@ Curl_http_output_auth(struct connectdata *conn, authhost = &data->state.authhost; authproxy = &data->state.authproxy; - if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) || - conn->bits.user_passwd || conn->oauth_bearer) + if( +#ifndef CURL_DISABLE_PROXY + (conn->bits.httpproxy && conn->bits.proxy_user_passwd) || +#endif + conn->bits.user_passwd || data->set.str[STRING_BEARER]) /* continue please */; else { authhost->done = TRUE; @@ -1062,8 +1087,10 @@ static int http_should_fail(struct connectdata *conn) */ if((httpcode == 401) && !conn->bits.user_passwd) return TRUE; +#ifndef CURL_DISABLE_PROXY if((httpcode == 407) && !conn->bits.proxy_user_passwd) return TRUE; +#endif return data->state.authproblem; } @@ -1120,45 +1147,20 @@ static size_t readmoredata(char *buffer, return fullsize; } -/* ------------------------------------------------------------------------- */ -/* add_buffer functions */ - -/* - * Curl_add_buffer_init() sets up and returns a fine buffer struct - */ -Curl_send_buffer *Curl_add_buffer_init(void) -{ - return calloc(1, sizeof(Curl_send_buffer)); -} - -/* - * Curl_add_buffer_free() frees all associated resources. - */ -void Curl_add_buffer_free(Curl_send_buffer **inp) -{ - Curl_send_buffer *in = *inp; - if(in) /* deal with NULL input */ - free(in->buffer); - free(in); - *inp = NULL; -} - /* - * Curl_add_buffer_send() sends a header buffer and frees all associated + * Curl_buffer_send() sends a header buffer and frees all associated * memory. Body data may be appended to the header data if desired. * * Returns CURLcode */ -CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, - struct connectdata *conn, - - /* add the number of sent bytes to this - counter */ - curl_off_t *bytes_written, - - /* how much of the buffer contains body data */ - size_t included_body_bytes, - int socketindex) +CURLcode Curl_buffer_send(struct dynbuf *in, + struct connectdata *conn, + /* add the number of sent bytes to this + counter */ + curl_off_t *bytes_written, + /* how much of the buffer contains body data */ + size_t included_body_bytes, + int socketindex) { ssize_t amount; CURLcode result; @@ -1169,7 +1171,6 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, size_t sendsize; curl_socket_t sockfd; size_t headersize; - Curl_send_buffer *in = *inp; DEBUGASSERT(socketindex <= SECONDARYSOCKET); @@ -1178,8 +1179,8 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, /* The looping below is required since we use non-blocking sockets, but due to the circumstances we will just loop and try again and again etc */ - ptr = in->buffer; - size = in->size_used; + ptr = Curl_dyn_ptr(in); + size = Curl_dyn_len(in); headersize = size - included_body_bytes; /* the initial part that isn't body is header */ @@ -1190,12 +1191,15 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, /* Curl_convert_to_network calls failf if unsuccessful */ if(result) { /* conversion failed, free memory and return to the caller */ - Curl_add_buffer_free(inp); + Curl_dyn_free(in); return result; } - if((conn->handler->flags & PROTOPT_SSL || - conn->http_proxy.proxytype == CURLPROXY_HTTPS) + if((conn->handler->flags & PROTOPT_SSL +#ifndef CURL_DISABLE_PROXY + || conn->http_proxy.proxytype == CURLPROXY_HTTPS +#endif + ) && conn->httpversion != 20) { /* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk when we speak HTTPS, as if only a fraction of it is sent now, this data @@ -1214,14 +1218,27 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, result = Curl_get_upload_buffer(data); if(result) { /* malloc failed, free memory and return to the caller */ - Curl_add_buffer_free(&in); + Curl_dyn_free(in); return result; } memcpy(data->state.ulbuf, ptr, sendsize); ptr = data->state.ulbuf; } - else + else { +#ifdef CURLDEBUG + /* Allow debug builds override this logic to force short initial sends */ + char *p = getenv("CURL_SMALLREQSEND"); + if(p) { + size_t altsize = (size_t)strtoul(p, NULL, 10); + if(altsize) + sendsize = CURLMIN(size, altsize); + else + sendsize = size; + } + else +#endif sendsize = size; + } result = Curl_write(conn, sockfd, ptr, sendsize, &amount); @@ -1264,7 +1281,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, size -= amount; - ptr = in->buffer + amount; + ptr = Curl_dyn_ptr(in) + amount; /* backup the currently set pointers */ http->backup.fread_func = data->state.fread_func; @@ -1278,7 +1295,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, http->postdata = ptr; http->postsize = (curl_off_t)size; - http->send_buffer = in; + http->send_buffer = *in; /* copy the whole struct */ http->sending = HTTPSEND_REQUEST; return CURLE_OK; @@ -1298,92 +1315,11 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, return CURLE_SEND_ERROR; } } - Curl_add_buffer_free(&in); + Curl_dyn_free(in); return result; } - -/* - * add_bufferf() add the formatted input to the buffer. - */ -CURLcode Curl_add_bufferf(Curl_send_buffer **inp, const char *fmt, ...) -{ - char *s; - va_list ap; - Curl_send_buffer *in = *inp; - va_start(ap, fmt); - s = vaprintf(fmt, ap); /* this allocs a new string to append */ - va_end(ap); - - if(s) { - CURLcode result = Curl_add_buffer(inp, s, strlen(s)); - free(s); - return result; - } - /* If we failed, we cleanup the whole buffer and return error */ - free(in->buffer); - free(in); - *inp = NULL; - return CURLE_OUT_OF_MEMORY; -} - -/* - * Curl_add_buffer() appends a memory chunk to the existing buffer - */ -CURLcode Curl_add_buffer(Curl_send_buffer **inp, const void *inptr, - size_t size) -{ - char *new_rb; - Curl_send_buffer *in = *inp; - - if(~size < in->size_used) { - /* If resulting used size of send buffer would wrap size_t, cleanup - the whole buffer and return error. Otherwise the required buffer - size will fit into a single allocatable memory chunk */ - Curl_safefree(in->buffer); - free(in); - *inp = NULL; - return CURLE_OUT_OF_MEMORY; - } - - if(!in->buffer || - ((in->size_used + size) > (in->size_max - 1))) { - /* If current buffer size isn't enough to hold the result, use a - buffer size that doubles the required size. If this new size - would wrap size_t, then just use the largest possible one */ - size_t new_size; - - if((size > (size_t)-1 / 2) || (in->size_used > (size_t)-1 / 2) || - (~(size * 2) < (in->size_used * 2))) - new_size = (size_t)-1; - else - new_size = (in->size_used + size) * 2; - - if(in->buffer) - /* we have a buffer, enlarge the existing one */ - new_rb = Curl_saferealloc(in->buffer, new_size); - else - /* create a new buffer */ - new_rb = malloc(new_size); - - if(!new_rb) { - /* If we failed, we cleanup the whole buffer and return error */ - free(in); - *inp = NULL; - return CURLE_OUT_OF_MEMORY; - } - - in->buffer = new_rb; - in->size_max = new_size; - } - memcpy(&in->buffer[in->size_used], inptr, size); - - in->size_used += size; - - return CURLE_OK; -} - /* end of the add_buffer functions */ /* ------------------------------------------------------------------------- */ @@ -1456,6 +1392,7 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done) function to make the re-use checks properly be able to check this bit. */ connkeep(conn, "HTTP default"); +#ifndef CURL_DISABLE_PROXY /* the CONNECT procedure might not have been completed */ result = Curl_proxy_connect(conn, FIRSTSOCKET); if(result) @@ -1472,7 +1409,6 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done) /* nothing else to do except wait right now - we're not done here. */ return CURLE_OK; -#ifndef CURL_DISABLE_PROXY if(conn->data->set.haproxyprotocol) { /* add HAProxy PROXY protocol header */ result = add_haproxy_protocol_header(conn); @@ -1497,11 +1433,9 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done) interface and then we're always _sending_ a request and thus we wait for the single socket to become writable only */ static int http_getsock_do(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { /* write mode */ - (void)numsocks; /* unused, we trust it to be at least 1 */ socks[0] = conn->sock[FIRSTSOCKET]; return GETSOCK_WRITESOCK(0); } @@ -1510,7 +1444,7 @@ static int http_getsock_do(struct connectdata *conn, static CURLcode add_haproxy_protocol_header(struct connectdata *conn) { char proxy_header[128]; - Curl_send_buffer *req_buffer; + struct dynbuf req; CURLcode result; char tcp_version[5]; @@ -1531,19 +1465,14 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn) conn->data->info.conn_local_port, conn->data->info.conn_primary_port); - req_buffer = Curl_add_buffer_init(); - if(!req_buffer) - return CURLE_OUT_OF_MEMORY; + Curl_dyn_init(&req, DYN_HAXPROXY); - result = Curl_add_bufferf(&req_buffer, proxy_header); + result = Curl_dyn_add(&req, proxy_header); if(result) return result; - result = Curl_add_buffer_send(&req_buffer, - conn, - &conn->data->info.request_size, - 0, - FIRSTSOCKET); + result = Curl_buffer_send(&req, conn, &conn->data->info.request_size, + 0, FIRSTSOCKET); return result; } @@ -1555,6 +1484,13 @@ static CURLcode https_connecting(struct connectdata *conn, bool *done) CURLcode result; DEBUGASSERT((conn) && (conn->handler->flags & PROTOPT_SSL)); +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) { + *done = TRUE; + return CURLE_OK; + } +#endif + /* perform SSL initialization for this socket */ result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done); if(result) @@ -1564,11 +1500,10 @@ static CURLcode https_connecting(struct connectdata *conn, bool *done) } static int https_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { if(conn->handler->flags & PROTOPT_SSL) - return Curl_ssl_getsock(conn, socks, numsocks); + return Curl_ssl_getsock(conn, socks); return GETSOCK_BLANK; } #endif /* USE_SSL */ @@ -1598,13 +1533,11 @@ CURLcode Curl_http_done(struct connectdata *conn, if(!http) return CURLE_OK; - if(http->send_buffer) { - Curl_add_buffer_free(&http->send_buffer); - } - - Curl_http2_done(conn, premature); - + Curl_dyn_free(&http->send_buffer); + Curl_http2_done(data, premature); + Curl_quic_done(data, premature); Curl_mime_cleanpart(&http->form); + Curl_dyn_reset(&data->state.headerb); if(status) return status; @@ -1650,6 +1583,12 @@ static bool use_http_1_1plus(const struct Curl_easy *data, static const char *get_http_string(const struct Curl_easy *data, const struct connectdata *conn) { +#ifdef ENABLE_QUIC + if((data->set.httpversion == CURL_HTTP_VERSION_3) || + (conn->httpversion == 30)) + return "3"; +#endif + #ifdef USE_NGHTTP2 if(conn->proto.httpc.h2) return "2"; @@ -1664,13 +1603,13 @@ static const char *get_http_string(const struct Curl_easy *data, /* check and possibly add an Expect: header */ static CURLcode expect100(struct Curl_easy *data, struct connectdata *conn, - Curl_send_buffer *req_buffer) + struct dynbuf *req) { CURLcode result = CURLE_OK; data->state.expect100header = FALSE; /* default to false unless it is set to TRUE below */ - if(use_http_1_1plus(data, conn) && - (conn->httpversion != 20)) { + if(!data->state.disableexpect && use_http_1_1plus(data, conn) && + (conn->httpversion < 20)) { /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an Expect: 100-continue to the headers which actually speeds up post operations (as there is one packet coming back from the web server) */ @@ -1680,8 +1619,7 @@ static CURLcode expect100(struct Curl_easy *data, Curl_compareheader(ptr, "Expect:", "100-continue"); } else { - result = Curl_add_bufferf(&req_buffer, - "Expect: 100-continue\r\n"); + result = Curl_dyn_add(req, "Expect: 100-continue\r\n"); if(!result) data->state.expect100header = TRUE; } @@ -1700,7 +1638,7 @@ enum proxy_use { will return an error code if one of the headers is not formatted correctly */ CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, - Curl_send_buffer *buffer, + struct dynbuf *b, struct Curl_easy *handle) { char *ptr = NULL; @@ -1726,8 +1664,10 @@ CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, /* only add correctly formatted trailers */ ptr = strchr(trailers->data, ':'); if(ptr && *(ptr + 1) == ' ') { - result = Curl_add_bufferf(&buffer, "%s%s", trailers->data, - endofline_native); + result = Curl_dyn_add(b, trailers->data); + if(result) + return result; + result = Curl_dyn_add(b, endofline_native); if(result) return result; } @@ -1735,14 +1675,13 @@ CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, infof(handle, "Malformatted trailing header ! Skipping trailer."); trailers = trailers->next; } - result = Curl_add_buffer(&buffer, endofline_network, - strlen(endofline_network)); + result = Curl_dyn_add(b, endofline_network); return result; } CURLcode Curl_add_custom_headers(struct connectdata *conn, bool is_connect, - Curl_send_buffer *req_buffer) + struct dynbuf *req) { char *ptr; struct curl_slist *h[2]; @@ -1751,6 +1690,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, struct Curl_easy *data = conn->data; int i; +#ifndef CURL_DISABLE_PROXY enum proxy_use proxy; if(is_connect) @@ -1777,6 +1717,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, h[0] = data->set.headers; break; } +#else + (void)is_connect; + h[0] = data->set.headers; +#endif /* loop through one or two lists */ for(i = 0; i < numlists; i++) { @@ -1804,7 +1748,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, /* copy the source */ semicolonp = strdup(headers->data); if(!semicolonp) { - Curl_add_buffer_free(&req_buffer); + Curl_dyn_free(req); return CURLE_OUT_OF_MEMORY; } /* put a colon where the semicolon is */ @@ -1828,16 +1772,16 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, CURLcode result = CURLE_OK; char *compare = semicolonp ? semicolonp : headers->data; - if(conn->allocptr.host && + if(data->state.aptr.host && /* a Host: header was sent already, don't pass on any custom Host: header as that will produce *two* in the same request! */ checkprefix("Host:", compare)) ; - else if(data->set.httpreq == HTTPREQ_POST_FORM && + else if(data->state.httpreq == HTTPREQ_POST_FORM && /* this header (extended by formdata.c) is sent later */ checkprefix("Content-Type:", compare)) ; - else if(data->set.httpreq == HTTPREQ_POST_MIME && + else if(data->state.httpreq == HTTPREQ_POST_MIME && /* this header is sent later */ checkprefix("Content-Type:", compare)) ; @@ -1846,12 +1790,12 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, we will force length zero then */ checkprefix("Content-Length:", compare)) ; - else if(conn->allocptr.te && + else if(data->state.aptr.te && /* when asking for Transfer-Encoding, don't pass on a custom Connection: */ checkprefix("Connection:", compare)) ; - else if((conn->httpversion == 20) && + else if((conn->httpversion >= 20) && checkprefix("Transfer-Encoding:", compare)) /* HTTP/2 doesn't support chunked requests */ ; @@ -1865,7 +1809,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, !strcasecompare(data->state.first_host, conn->host.name))) ; else { - result = Curl_add_bufferf(&req_buffer, "%s\r\n", compare); + result = Curl_dyn_addf(req, "%s\r\n", compare); } if(semicolonp) free(semicolonp); @@ -1881,9 +1825,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, } #ifndef CURL_DISABLE_PARSEDATE -CURLcode Curl_add_timecondition(struct Curl_easy *data, - Curl_send_buffer *req_buffer) +CURLcode Curl_add_timecondition(const struct connectdata *conn, + struct dynbuf *req) { + struct Curl_easy *data = conn->data; const struct tm *tm; struct tm keeptime; CURLcode result; @@ -1916,6 +1861,11 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, break; } + if(Curl_checkheaders(conn, condp)) { + /* A custom header was specified; it will be sent instead. */ + return CURLE_OK; + } + /* The If-Modified-Since header family should have their times set in * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be * represented in Greenwich Mean Time (GMT), without exception. For the @@ -1935,17 +1885,17 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, tm->tm_min, tm->tm_sec); - result = Curl_add_buffer(&req_buffer, datestr, strlen(datestr)); + result = Curl_dyn_add(req, datestr); return result; } #else /* disabled */ -CURLcode Curl_add_timecondition(struct Curl_easy *data, - Curl_send_buffer *req_buffer) +CURLcode Curl_add_timecondition(const struct connectdata *conn, + struct dynbuf *req) { - (void)data; - (void)req_buffer; + (void)conn; + (void)req; return CURLE_OK; } #endif @@ -1968,63 +1918,66 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) const char *te = ""; /* transfer-encoding */ const char *ptr; const char *request; - Curl_HttpReq httpreq = data->set.httpreq; + Curl_HttpReq httpreq = data->state.httpreq; #if !defined(CURL_DISABLE_COOKIES) char *addcookies = NULL; #endif curl_off_t included_body = 0; const char *httpstring; - Curl_send_buffer *req_buffer; + struct dynbuf req; curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */ + char *altused = NULL; /* Always consider the DO phase done after this function call, even if there may be parts of the request that is not yet sent, since we can deal with the rest of the request in the PERFORM phase. */ *done = TRUE; - if(conn->httpversion < 20) { /* unless the connection is re-used and already - http2 */ - switch(conn->negnpn) { - case CURL_HTTP_VERSION_2: - conn->httpversion = 20; /* we know we're on HTTP/2 now */ - - result = Curl_http2_switched(conn, NULL, 0); - if(result) - return result; - break; - case CURL_HTTP_VERSION_1_1: - /* continue with HTTP/1.1 when explicitly requested */ - break; - default: - /* Check if user wants to use HTTP/2 with clear TCP*/ -#ifdef USE_NGHTTP2 - if(conn->data->set.httpversion == - CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - /* We don't support HTTP/2 proxies yet. Also it's debatable whether - or not this setting should apply to HTTP/2 proxies. */ - infof(data, "Ignoring HTTP/2 prior knowledge due to proxy\n"); - break; - } - - DEBUGF(infof(data, "HTTP/2 over clean TCP\n")); - conn->httpversion = 20; + if(conn->transport != TRNSPRT_QUIC) { + if(conn->httpversion < 20) { /* unless the connection is re-used and + already http2 */ + switch(conn->negnpn) { + case CURL_HTTP_VERSION_2: + conn->httpversion = 20; /* we know we're on HTTP/2 now */ result = Curl_http2_switched(conn, NULL, 0); if(result) return result; - } + break; + case CURL_HTTP_VERSION_1_1: + /* continue with HTTP/1.1 when explicitly requested */ + break; + default: + /* Check if user wants to use HTTP/2 with clear TCP*/ +#ifdef USE_NGHTTP2 + if(conn->data->set.httpversion == + CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + /* We don't support HTTP/2 proxies yet. Also it's debatable + whether or not this setting should apply to HTTP/2 proxies. */ + infof(data, "Ignoring HTTP/2 prior knowledge due to proxy\n"); + break; + } #endif - break; + DEBUGF(infof(data, "HTTP/2 over clean TCP\n")); + conn->httpversion = 20; + + result = Curl_http2_switched(conn, NULL, 0); + if(result) + return result; + } +#endif + break; + } + } + else { + /* prepare for a http2 request */ + result = Curl_http2_setup(conn); + if(result) + return result; } } - else { - /* prepare for a http2 request */ - result = Curl_http2_setup(conn); - if(result) - return result; - } - http = data->req.protop; DEBUGASSERT(http); @@ -2080,8 +2033,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) with the user-agent string specified, we erase the previously made string here. */ if(Curl_checkheaders(conn, "User-Agent")) { - free(conn->allocptr.uagent); - conn->allocptr.uagent = NULL; + free(data->state.aptr.uagent); + data->state.aptr.uagent = NULL; } /* setup the authentication headers */ @@ -2109,14 +2062,14 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) else conn->bits.authneg = FALSE; - Curl_safefree(conn->allocptr.ref); + Curl_safefree(data->state.aptr.ref); if(data->change.referer && !Curl_checkheaders(conn, "Referer")) { - conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer); - if(!conn->allocptr.ref) + data->state.aptr.ref = aprintf("Referer: %s\r\n", data->change.referer); + if(!data->state.aptr.ref) return CURLE_OUT_OF_MEMORY; } else - conn->allocptr.ref = NULL; + data->state.aptr.ref = NULL; #if !defined(CURL_DISABLE_COOKIES) if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie")) @@ -2125,15 +2078,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(!Curl_checkheaders(conn, "Accept-Encoding") && data->set.str[STRING_ENCODING]) { - Curl_safefree(conn->allocptr.accept_encoding); - conn->allocptr.accept_encoding = + Curl_safefree(data->state.aptr.accept_encoding); + data->state.aptr.accept_encoding = aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); - if(!conn->allocptr.accept_encoding) + if(!data->state.aptr.accept_encoding) return CURLE_OUT_OF_MEMORY; } else { - Curl_safefree(conn->allocptr.accept_encoding); - conn->allocptr.accept_encoding = NULL; + Curl_safefree(data->state.aptr.accept_encoding); + data->state.aptr.accept_encoding = NULL; } #ifdef HAVE_LIBZ @@ -2149,7 +2102,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) char *cptr = Curl_checkheaders(conn, "Connection"); #define TE_HEADER "TE: gzip\r\n" - Curl_safefree(conn->allocptr.te); + Curl_safefree(data->state.aptr.te); if(cptr) { cptr = Curl_copy_header_value(cptr); @@ -2158,11 +2111,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } /* Create the (updated) Connection: header */ - conn->allocptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER, + data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER, cptr ? cptr : "", (cptr && *cptr) ? ", ":""); free(cptr); - if(!conn->allocptr.te) + if(!data->state.aptr.te) return CURLE_OUT_OF_MEMORY; } #endif @@ -2220,14 +2173,16 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) else { if((conn->handler->protocol & PROTO_FAMILY_HTTP) && (((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) && - http->postsize < 0) || - (data->set.upload && data->state.infilesize == -1))) { + http->postsize < 0) || + ((data->set.upload || httpreq == HTTPREQ_POST) && + data->state.infilesize == -1))) { if(conn->bits.authneg) /* don't enable chunked during auth neg */ ; else if(use_http_1_1plus(data, conn)) { - /* HTTP, upload, unknown file size and not HTTP 1.0 */ - data->req.upload_chunky = TRUE; + if(conn->httpversion < 20) + /* HTTP, upload, unknown file size and not HTTP 1.0 */ + data->req.upload_chunky = TRUE; } else { failf(data, "Chunky upload is not supported by HTTP 1.0"); @@ -2243,7 +2198,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) te = "Transfer-Encoding: chunked\r\n"; } - Curl_safefree(conn->allocptr.host); + Curl_safefree(data->state.aptr.host); ptr = Curl_checkheaders(conn, "Host"); if(ptr && (!data->state.this_is_a_follow || @@ -2278,19 +2233,19 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(colon) *colon = 0; /* The host must not include an embedded port number */ } - Curl_safefree(conn->allocptr.cookiehost); - conn->allocptr.cookiehost = cookiehost; + Curl_safefree(data->state.aptr.cookiehost); + data->state.aptr.cookiehost = cookiehost; } #endif if(strcmp("Host:", ptr)) { - conn->allocptr.host = aprintf("Host:%s\r\n", &ptr[5]); - if(!conn->allocptr.host) + data->state.aptr.host = aprintf("Host:%s\r\n", &ptr[5]); + if(!data->state.aptr.host) return CURLE_OUT_OF_MEMORY; } else /* when clearing the header */ - conn->allocptr.host = NULL; + data->state.aptr.host = NULL; } else { /* When building Host: headers, we must put the host name within @@ -2302,18 +2257,18 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) (conn->remote_port == PORT_HTTP)) ) /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include the port number in the host string */ - conn->allocptr.host = aprintf("Host: %s%s%s\r\n", + data->state.aptr.host = aprintf("Host: %s%s%s\r\n", conn->bits.ipv6_ip?"[":"", host, conn->bits.ipv6_ip?"]":""); else - conn->allocptr.host = aprintf("Host: %s%s%s:%d\r\n", + data->state.aptr.host = aprintf("Host: %s%s%s:%d\r\n", conn->bits.ipv6_ip?"[":"", host, conn->bits.ipv6_ip?"]":"", conn->remote_port); - if(!conn->allocptr.host) + if(!data->state.aptr.host) /* without Host: we can't make a nice request */ return CURLE_OUT_OF_MEMORY; } @@ -2328,7 +2283,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* and no fragment part */ CURLUcode uc; - char *url; CURLU *h = curl_url_dup(data->state.uh); if(!h) return CURLE_OUT_OF_MEMORY; @@ -2359,19 +2313,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return CURLE_OUT_OF_MEMORY; } } - /* now extract the new version of the URL */ - uc = curl_url_get(h, CURLUPART_URL, &url, 0); + /* Extract the URL to use in the request. Store in STRING_TEMP_URL for + clean-up reasons if the function returns before the free() further + down. */ + uc = curl_url_get(h, CURLUPART_URL, &data->set.str[STRING_TEMP_URL], 0); if(uc) { curl_url_cleanup(h); return CURLE_OUT_OF_MEMORY; } - if(data->change.url_alloc) - free(data->change.url); - - data->change.url = url; - data->change.url_alloc = TRUE; - curl_url_cleanup(h); if(strcasecompare("ftp", data->state.up.scheme)) { @@ -2399,7 +2349,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) data->set.prefer_ascii ? 'a' : 'i'); } } - if(conn->bits.user_passwd && !conn->bits.userpwd_in_url) + if(conn->bits.user_passwd) paste_ftp_userpwd = TRUE; } } @@ -2489,21 +2439,21 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) && !Curl_checkheaders(conn, "Range")) { /* if a line like this was already allocated, free the previous one */ - free(conn->allocptr.rangeline); - conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", + free(data->state.aptr.rangeline); + data->state.aptr.rangeline = aprintf("Range: bytes=%s\r\n", data->state.range); } else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) && !Curl_checkheaders(conn, "Content-Range")) { /* if a line like this was already allocated, free the previous one */ - free(conn->allocptr.rangeline); + free(data->state.aptr.rangeline); if(data->set.set_resume_from < 0) { /* Upload resume was asked for, but we don't know the size of the remote part so we tell the server (and act accordingly) that we upload the whole file (again) */ - conn->allocptr.rangeline = + data->state.aptr.rangeline = aprintf("Content-Range: bytes 0-%" CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T "\r\n", data->state.infilesize - 1, data->state.infilesize); @@ -2513,7 +2463,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* This is because "resume" was selected */ curl_off_t total_expected_size = data->state.resume_from + data->state.infilesize; - conn->allocptr.rangeline = + data->state.aptr.rangeline = aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T "\r\n", data->state.range, total_expected_size-1, @@ -2522,11 +2472,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) else { /* Range was selected and then we just pass the incoming range and append total size */ - conn->allocptr.rangeline = + data->state.aptr.rangeline = aprintf("Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n", data->state.range, data->state.infilesize); } - if(!conn->allocptr.rangeline) + if(!data->state.aptr.rangeline) return CURLE_OUT_OF_MEMORY; } } @@ -2534,14 +2484,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) httpstring = get_http_string(data, conn); /* initialize a dynamic send-buffer */ - req_buffer = Curl_add_buffer_init(); - - if(!req_buffer) - return CURLE_OUT_OF_MEMORY; + Curl_dyn_init(&req, DYN_HTTP_REQUEST); /* add the main request stuff */ /* GET/HEAD/POST/PUT */ - result = Curl_add_bufferf(&req_buffer, "%s ", request); + result = Curl_dyn_addf(&req, "%s ", request); if(result) return result; @@ -2550,72 +2497,92 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) query = NULL; } +#ifndef CURL_DISABLE_PROXY /* url */ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - char *url = data->change.url; - result = Curl_add_buffer(&req_buffer, url, strlen(url)); + char *url = data->set.str[STRING_TEMP_URL]; + result = Curl_dyn_add(&req, url); + Curl_safefree(data->set.str[STRING_TEMP_URL]); } - else if(paste_ftp_userpwd) - result = Curl_add_bufferf(&req_buffer, "ftp://%s:%s@%s", - conn->user, conn->passwd, - path + sizeof("ftp://") - 1); + else +#endif + if(paste_ftp_userpwd) + result = Curl_dyn_addf(&req, "ftp://%s:%s@%s", conn->user, conn->passwd, + path + sizeof("ftp://") - 1); else { - result = Curl_add_buffer(&req_buffer, path, strlen(path)); + result = Curl_dyn_add(&req, path); if(result) return result; if(query) - result = Curl_add_bufferf(&req_buffer, "?%s", query); + result = Curl_dyn_addf(&req, "?%s", query); } if(result) return result; +#ifdef USE_ALTSVC + if(conn->bits.altused && !Curl_checkheaders(conn, "Alt-Used")) { + altused = aprintf("Alt-Used: %s:%d\r\n", + conn->conn_to_host.name, conn->conn_to_port); + if(!altused) { + Curl_dyn_free(&req); + return CURLE_OUT_OF_MEMORY; + } + } +#endif result = - Curl_add_bufferf(&req_buffer, - "%s" /* ftp typecode (;type=x) */ - " HTTP/%s\r\n" /* HTTP version */ - "%s" /* host */ - "%s" /* proxyuserpwd */ - "%s" /* userpwd */ - "%s" /* range */ - "%s" /* user agent */ - "%s" /* accept */ - "%s" /* TE: */ - "%s" /* accept-encoding */ - "%s" /* referer */ - "%s" /* Proxy-Connection */ - "%s",/* transfer-encoding */ - - ftp_typecode, - httpstring, - (conn->allocptr.host?conn->allocptr.host:""), - conn->allocptr.proxyuserpwd? - conn->allocptr.proxyuserpwd:"", - conn->allocptr.userpwd?conn->allocptr.userpwd:"", - (data->state.use_range && conn->allocptr.rangeline)? - conn->allocptr.rangeline:"", - (data->set.str[STRING_USERAGENT] && - *data->set.str[STRING_USERAGENT] && - conn->allocptr.uagent)? - conn->allocptr.uagent:"", - http->p_accept?http->p_accept:"", - conn->allocptr.te?conn->allocptr.te:"", - (data->set.str[STRING_ENCODING] && - *data->set.str[STRING_ENCODING] && - conn->allocptr.accept_encoding)? - conn->allocptr.accept_encoding:"", - (data->change.referer && conn->allocptr.ref)? - conn->allocptr.ref:"" /* Referer: */, - (conn->bits.httpproxy && - !conn->bits.tunnel_proxy && - !Curl_checkProxyheaders(conn, "Proxy-Connection"))? - "Proxy-Connection: Keep-Alive\r\n":"", - te + Curl_dyn_addf(&req, + "%s" /* ftp typecode (;type=x) */ + " HTTP/%s\r\n" /* HTTP version */ + "%s" /* host */ + "%s" /* proxyuserpwd */ + "%s" /* userpwd */ + "%s" /* range */ + "%s" /* user agent */ + "%s" /* accept */ + "%s" /* TE: */ + "%s" /* accept-encoding */ + "%s" /* referer */ + "%s" /* Proxy-Connection */ + "%s" /* transfer-encoding */ + "%s",/* Alt-Used */ + + ftp_typecode, + httpstring, + (data->state.aptr.host?data->state.aptr.host:""), + data->state.aptr.proxyuserpwd? + data->state.aptr.proxyuserpwd:"", + data->state.aptr.userpwd?data->state.aptr.userpwd:"", + (data->state.use_range && data->state.aptr.rangeline)? + data->state.aptr.rangeline:"", + (data->set.str[STRING_USERAGENT] && + *data->set.str[STRING_USERAGENT] && + data->state.aptr.uagent)? + data->state.aptr.uagent:"", + http->p_accept?http->p_accept:"", + data->state.aptr.te?data->state.aptr.te:"", + (data->set.str[STRING_ENCODING] && + *data->set.str[STRING_ENCODING] && + data->state.aptr.accept_encoding)? + data->state.aptr.accept_encoding:"", + (data->change.referer && data->state.aptr.ref)? + data->state.aptr.ref:"" /* Referer: */, +#ifndef CURL_DISABLE_PROXY + (conn->bits.httpproxy && + !conn->bits.tunnel_proxy && + !Curl_checkProxyheaders(conn, "Proxy-Connection"))? + "Proxy-Connection: Keep-Alive\r\n":"", +#else + "", +#endif + te, + altused ? altused : "" ); /* clear userpwd and proxyuserpwd to avoid re-using old credentials * from re-used connections */ - Curl_safefree(conn->allocptr.userpwd); - Curl_safefree(conn->allocptr.proxyuserpwd); + Curl_safefree(data->state.aptr.userpwd); + Curl_safefree(data->state.aptr.proxyuserpwd); + free(altused); if(result) return result; @@ -2625,7 +2592,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) (data->set.httpversion == CURL_HTTP_VERSION_2)) { /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done over SSL */ - result = Curl_http2_request_upgrade(req_buffer, conn); + result = Curl_http2_request_upgrade(&req, conn); if(result) return result; } @@ -2635,11 +2602,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) struct Cookie *co = NULL; /* no cookies from start */ int count = 0; - if(data->cookies) { + if(data->cookies && data->state.cookie_engine) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); co = Curl_cookie_getlist(data->cookies, - conn->allocptr.cookiehost? - conn->allocptr.cookiehost:host, + data->state.aptr.cookiehost? + data->state.aptr.cookiehost:host, data->state.up.path, (conn->handler->protocol&CURLPROTO_HTTPS)? TRUE:FALSE); @@ -2651,13 +2618,12 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) while(co) { if(co->value) { if(0 == count) { - result = Curl_add_bufferf(&req_buffer, "Cookie: "); + result = Curl_dyn_add(&req, "Cookie: "); if(result) break; } - result = Curl_add_bufferf(&req_buffer, - "%s%s=%s", count?"; ":"", - co->name, co->value); + result = Curl_dyn_addf(&req, "%s%s=%s", count?"; ":"", + co->name, co->value); if(result) break; count++; @@ -2668,26 +2634,25 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } if(addcookies && !result) { if(!count) - result = Curl_add_bufferf(&req_buffer, "Cookie: "); + result = Curl_dyn_add(&req, "Cookie: "); if(!result) { - result = Curl_add_bufferf(&req_buffer, "%s%s", count?"; ":"", - addcookies); + result = Curl_dyn_addf(&req, "%s%s", count?"; ":"", addcookies); count++; } } if(count && !result) - result = Curl_add_buffer(&req_buffer, "\r\n", 2); + result = Curl_dyn_add(&req, "\r\n"); if(result) return result; } #endif - result = Curl_add_timecondition(data, req_buffer); + result = Curl_add_timecondition(conn, &req); if(result) return result; - result = Curl_add_custom_headers(conn, FALSE, req_buffer); + result = Curl_add_custom_headers(conn, FALSE, &req); if(result) return result; @@ -2710,20 +2675,20 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if((postsize != -1) && !data->req.upload_chunky && (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) { /* only add Content-Length if not uploading chunked */ - result = Curl_add_bufferf(&req_buffer, - "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", postsize); + result = Curl_dyn_addf(&req, "Content-Length: %" CURL_FORMAT_CURL_OFF_T + "\r\n", postsize); if(result) return result; } if(postsize != 0) { - result = expect100(data, conn, req_buffer); + result = expect100(data, conn, &req); if(result) return result; } - result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers */ + /* end of headers */ + result = Curl_dyn_add(&req, "\r\n"); if(result) return result; @@ -2731,8 +2696,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) Curl_pgrsSetUploadSize(data, postsize); /* this sends the buffer and frees all the buffer resources */ - result = Curl_add_buffer_send(&req_buffer, conn, - &data->info.request_size, 0, FIRSTSOCKET); + result = Curl_buffer_send(&req, conn, &data->info.request_size, 0, + FIRSTSOCKET); if(result) failf(data, "Failed sending PUT request"); else @@ -2748,12 +2713,12 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* This is form posting using mime data. */ if(conn->bits.authneg) { /* nothing to post! */ - result = Curl_add_bufferf(&req_buffer, "Content-Length: 0\r\n\r\n"); + result = Curl_dyn_add(&req, "Content-Length: 0\r\n\r\n"); if(result) return result; - result = Curl_add_buffer_send(&req_buffer, conn, - &data->info.request_size, 0, FIRSTSOCKET); + result = Curl_buffer_send(&req, conn, &data->info.request_size, 0, + FIRSTSOCKET); if(result) failf(data, "Failed sending POST request"); else @@ -2771,9 +2736,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) { /* we allow replacing this header if not during auth negotiation, although it isn't very wise to actually set your own */ - result = Curl_add_bufferf(&req_buffer, - "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", postsize); + result = Curl_dyn_addf(&req, + "Content-Length: %" CURL_FORMAT_CURL_OFF_T + "\r\n", postsize); if(result) return result; } @@ -2784,7 +2749,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) struct curl_slist *hdr; for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) { - result = Curl_add_bufferf(&req_buffer, "%s\r\n", hdr->data); + result = Curl_dyn_addf(&req, "%s\r\n", hdr->data); if(result) return result; } @@ -2801,7 +2766,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) Curl_compareheader(ptr, "Expect:", "100-continue"); } else if(postsize > EXPECT_100_THRESHOLD || postsize < 0) { - result = expect100(data, conn, req_buffer); + result = expect100(data, conn, &req); if(result) return result; } @@ -2809,7 +2774,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) data->state.expect100header = FALSE; /* make the request end in a true CRLF */ - result = Curl_add_buffer(&req_buffer, "\r\n", 2); + result = Curl_dyn_add(&req, "\r\n"); if(result) return result; @@ -2822,8 +2787,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) http->sending = HTTPSEND_BODY; /* this sends the buffer and frees all the buffer resources */ - result = Curl_add_buffer_send(&req_buffer, conn, - &data->info.request_size, 0, FIRSTSOCKET); + result = Curl_buffer_send(&req, conn, &data->info.request_size, 0, + FIRSTSOCKET); if(result) failf(data, "Failed sending POST request"); else @@ -2851,17 +2816,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) { /* we allow replacing this header if not during auth negotiation, although it isn't very wise to actually set your own */ - result = Curl_add_bufferf(&req_buffer, - "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", postsize); + result = Curl_dyn_addf(&req, "Content-Length: %" CURL_FORMAT_CURL_OFF_T + "\r\n", postsize); if(result) return result; } if(!Curl_checkheaders(conn, "Content-Type")) { - result = Curl_add_bufferf(&req_buffer, - "Content-Type: application/" - "x-www-form-urlencoded\r\n"); + result = Curl_dyn_add(&req, "Content-Type: application/" + "x-www-form-urlencoded\r\n"); if(result) return result; } @@ -2876,7 +2839,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) Curl_compareheader(ptr, "Expect:", "100-continue"); } else if(postsize > EXPECT_100_THRESHOLD || postsize < 0) { - result = expect100(data, conn, req_buffer); + result = expect100(data, conn, &req); if(result) return result; } @@ -2897,31 +2860,32 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) is no magic limit but only set to prevent really huge POSTs to get the data duplicated with malloc() and family. */ - result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers! */ + /* end of headers! */ + result = Curl_dyn_add(&req, "\r\n"); if(result) return result; if(!data->req.upload_chunky) { /* We're not sending it 'chunked', append it to the request already now to reduce the number if send() calls */ - result = Curl_add_buffer(&req_buffer, data->set.postfields, - (size_t)postsize); + result = Curl_dyn_addn(&req, data->set.postfields, + (size_t)postsize); included_body = postsize; } else { if(postsize) { /* Append the POST data chunky-style */ - result = Curl_add_bufferf(&req_buffer, "%x\r\n", (int)postsize); + result = Curl_dyn_addf(&req, "%x\r\n", (int)postsize); if(!result) { - result = Curl_add_buffer(&req_buffer, data->set.postfields, - (size_t)postsize); + result = Curl_dyn_addn(&req, data->set.postfields, + (size_t)postsize); if(!result) - result = Curl_add_buffer(&req_buffer, "\r\n", 2); + result = Curl_dyn_add(&req, "\r\n"); included_body = postsize + 2; } } if(!result) - result = Curl_add_buffer(&req_buffer, "\x30\x0d\x0a\x0d\x0a", 5); + result = Curl_dyn_add(&req, "\x30\x0d\x0a\x0d\x0a"); /* 0 CR LF CR LF */ included_body += 5; } @@ -2943,21 +2907,22 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* set the upload size to the progress meter */ Curl_pgrsSetUploadSize(data, http->postsize); - result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers! */ + /* end of headers! */ + result = Curl_dyn_add(&req, "\r\n"); if(result) return result; } } else { - result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers! */ + /* end of headers! */ + result = Curl_dyn_add(&req, "\r\n"); if(result) return result; if(data->req.upload_chunky && conn->bits.authneg) { /* Chunky upload is selected and we're negotiating auth still, send end-of-data only */ - result = Curl_add_buffer(&req_buffer, - "\x30\x0d\x0a\x0d\x0a", 5); + result = Curl_dyn_add(&req, (char *)"\x30\x0d\x0a\x0d\x0a"); /* 0 CR LF CR LF */ if(result) return result; @@ -2977,8 +2942,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } } /* issue the request */ - result = Curl_add_buffer_send(&req_buffer, conn, &data->info.request_size, - (size_t)included_body, FIRSTSOCKET); + result = Curl_buffer_send(&req, conn, &data->info.request_size, + (size_t)included_body, FIRSTSOCKET); if(result) failf(data, "Failed sending HTTP POST request"); @@ -2988,23 +2953,24 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) break; default: - result = Curl_add_buffer(&req_buffer, "\r\n", 2); + result = Curl_dyn_add(&req, "\r\n"); if(result) return result; /* issue the request */ - result = Curl_add_buffer_send(&req_buffer, conn, - &data->info.request_size, 0, FIRSTSOCKET); + result = Curl_buffer_send(&req, conn, &data->info.request_size, 0, + FIRSTSOCKET); if(result) failf(data, "Failed sending HTTP request"); else /* HTTP GET/HEAD download: */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, - http->postdata?FIRSTSOCKET:-1); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); } if(result) return result; + if(!postsize && (http->sending != HTTPSEND_REQUEST)) + data->req.upload_done = TRUE; if(data->req.writebytecount) { /* if a request-body has been sent off, we make sure this progress is noted @@ -3138,52 +3104,10 @@ checkprotoprefix(struct Curl_easy *data, struct connectdata *conn, return checkhttpprefix(data, s, len); } -/* - * header_append() copies a chunk of data to the end of the already received - * header. We make sure that the full string fit in the allocated header - * buffer, or else we enlarge it. - */ -static CURLcode header_append(struct Curl_easy *data, - struct SingleRequest *k, - size_t length) -{ - size_t newsize = k->hbuflen + length; - if(newsize > CURL_MAX_HTTP_HEADER) { - /* The 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 infinitely */ - failf(data, "Rejected %zu bytes header (max is %d)!", newsize, - CURL_MAX_HTTP_HEADER); - return CURLE_OUT_OF_MEMORY; - } - if(newsize >= data->state.headersize) { - /* We enlarge the header buffer as it is too small */ - char *newbuff; - size_t hbufp_index; - - newsize = CURLMAX((k->hbuflen + length) * 3 / 2, data->state.headersize*2); - hbufp_index = k->hbufp - data->state.headerbuff; - newbuff = realloc(data->state.headerbuff, newsize); - if(!newbuff) { - failf(data, "Failed to alloc memory for big header!"); - return CURLE_OUT_OF_MEMORY; - } - data->state.headersize = newsize; - data->state.headerbuff = newbuff; - k->hbufp = data->state.headerbuff + hbufp_index; - } - memcpy(k->hbufp, k->str_start, length); - k->hbufp += length; - k->hbuflen += length; - *k->hbufp = 0; - - return CURLE_OK; -} - static void print_http_error(struct Curl_easy *data) { struct SingleRequest *k = &data->req; - char *beg = k->p; + char *beg = Curl_dyn_ptr(&data->state.headerb); /* make sure that data->req.p points to the HTTP status line */ if(!strncmp(beg, "HTTP", 4)) { @@ -3221,14 +3145,17 @@ static void print_http_error(struct Curl_easy *data) * Read any HTTP header lines from the server and pass them to the client app. */ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, - struct connectdata *conn, - ssize_t *nread, - bool *stop_reading) + struct connectdata *conn, + ssize_t *nread, + bool *stop_reading) { CURLcode result; struct SingleRequest *k = &data->req; ssize_t onread = *nread; char *ostr = k->str; + char *headp; + char *str_start; + char *end_ptr; /* header line within buffer loop */ do { @@ -3237,22 +3164,25 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, int writetype; /* str_start is start of line within buf */ - k->str_start = k->str; + str_start = k->str; /* data is in network encoding so use 0x0a instead of '\n' */ - k->end_ptr = memchr(k->str_start, 0x0a, *nread); + end_ptr = memchr(str_start, 0x0a, *nread); - if(!k->end_ptr) { + if(!end_ptr) { /* Not a complete header line within buffer, append the data to the end of the headerbuff. */ - result = header_append(data, k, *nread); + result = Curl_dyn_addn(&data->state.headerb, str_start, *nread); if(result) return result; if(!k->headerline) { /* check if this looks like a protocol header */ - statusline st = checkprotoprefix(data, conn, data->state.headerbuff, - k->hbuflen); + statusline st = + checkprotoprefix(data, conn, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); + if(st == STATUS_BAD) { /* this is not the beginning of a protocol first header line */ k->header = FALSE; @@ -3270,28 +3200,26 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } /* decrease the size of the remaining (supposed) header line */ - rest_length = (k->end_ptr - k->str) + 1; + rest_length = (end_ptr - k->str) + 1; *nread -= (ssize_t)rest_length; - k->str = k->end_ptr + 1; /* move past new line */ + k->str = end_ptr + 1; /* move past new line */ - full_length = k->str - k->str_start; + full_length = k->str - str_start; - result = header_append(data, k, full_length); + result = Curl_dyn_addn(&data->state.headerb, str_start, full_length); if(result) return result; - k->end_ptr = k->hbufp; - k->p = data->state.headerbuff; - /**** - * We now have a FULL header line that p points to + * We now have a FULL header line in 'headerb'. *****/ if(!k->headerline) { /* the first read header */ - statusline st = checkprotoprefix(data, conn, data->state.headerbuff, - k->hbuflen); + statusline st = checkprotoprefix(data, conn, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); if(st == STATUS_BAD) { streamclose(conn, "bad HTTP: No end-of-message indicator"); /* this is not the beginning of a protocol first header line */ @@ -3314,26 +3242,27 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } - /* headers are in network encoding so - use 0x0a and 0x0d instead of '\n' and '\r' */ - if((0x0a == *k->p) || (0x0d == *k->p)) { + /* headers are in network encoding so use 0x0a and 0x0d instead of '\n' + and '\r' */ + headp = Curl_dyn_ptr(&data->state.headerb); + if((0x0a == *headp) || (0x0d == *headp)) { size_t headerlen; /* Zero-length header line means end of headers! */ #ifdef CURL_DOES_CONVERSIONS - if(0x0d == *k->p) { - *k->p = '\r'; /* replace with CR in host encoding */ - k->p++; /* pass the CR byte */ + if(0x0d == *headp) { + *headp = '\r'; /* replace with CR in host encoding */ + headp++; /* pass the CR byte */ } - if(0x0a == *k->p) { - *k->p = '\n'; /* replace with LF in host encoding */ - k->p++; /* pass the LF byte */ + if(0x0a == *headp) { + *headp = '\n'; /* replace with LF in host encoding */ + headp++; /* pass the LF byte */ } #else - if('\r' == *k->p) - k->p++; /* pass the \r byte */ - if('\n' == *k->p) - k->p++; /* pass the \n byte */ + if('\r' == *headp) + headp++; /* pass the \r byte */ + if('\n' == *headp) + headp++; /* pass the \n byte */ #endif /* CURL_DOES_CONVERSIONS */ if(100 <= k->httpcode && 199 >= k->httpcode) { @@ -3394,7 +3323,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if((k->size == -1) && !k->chunk && !conn->bits.close && (conn->httpversion == 11) && !(conn->handler->protocol & CURLPROTO_RTSP) && - data->set.httpreq != HTTPREQ_HEAD) { + 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 @@ -3452,10 +3381,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(data->set.include_header) writetype |= CLIENTWRITE_BODY; - headerlen = k->p - data->state.headerbuff; - + headerlen = Curl_dyn_len(&data->state.headerb); result = Curl_client_write(conn, writetype, - data->state.headerbuff, + Curl_dyn_ptr(&data->state.headerb), headerlen); if(result) return result; @@ -3490,7 +3418,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * continue sending even if it gets discarded */ - switch(data->set.httpreq) { + switch(data->state.httpreq) { case HTTPREQ_PUT: case HTTPREQ_POST: case HTTPREQ_POST_FORM: @@ -3501,7 +3429,16 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, */ Curl_expire_done(data, EXPIRE_100_TIMEOUT); if(!k->upload_done) { - if(data->set.http_keep_sending_on_error) { + if((k->httpcode == 417) && data->state.expect100header) { + /* 417 Expectation Failed - try again without the Expect + header */ + infof(data, "Got 417 while waiting for a 100\n"); + data->state.disableexpect = TRUE; + DEBUGASSERT(!data->req.newurl); + data->req.newurl = strdup(conn->data->change.url); + Curl_done_sending(conn, k); + } + else if(data->set.http_keep_sending_on_error) { infof(data, "HTTP error before end of send, keep sending\n"); if(k->exp100 > EXP100_SEND_DATA) { k->exp100 = EXP100_SEND_DATA; @@ -3511,8 +3448,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, else { infof(data, "HTTP error before end of send, stop sending\n"); streamclose(conn, "Stop sending data before everything sent"); + result = Curl_done_sending(conn, k); + if(result) + return result; k->upload_done = TRUE; - k->keepon &= ~KEEP_SEND; /* don't send */ if(data->state.expect100header) k->exp100 = EXP100_FAILED; } @@ -3597,14 +3536,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(data->set.verbose) Curl_debug(data, CURLINFO_HEADER_IN, - k->str_start, headerlen); + str_start, headerlen); break; /* exit header line loop */ } - /* We continue reading headers, so reset the line-based - header parsing variables hbufp && hbuflen */ - k->hbufp = data->state.headerbuff; - k->hbuflen = 0; + /* We continue reading headers, reset the line-based header */ + Curl_dyn_reset(&data->state.headerb); continue; } @@ -3623,12 +3560,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, #define SCRATCHSIZE 21 CURLcode res; char scratch[SCRATCHSIZE + 1]; /* "HTTP/major.minor 123" */ - /* We can't really convert this yet because we - don't know if it's the 1st header line or the body. - So we do a partial conversion into a scratch area, - leaving the data at k->p as-is. + /* We can't really convert this yet because we don't know if it's the + 1st header line or the body. So we do a partial conversion into a + scratch area, leaving the data at 'headp' as-is. */ - strncpy(&scratch[0], k->p, SCRATCHSIZE); + strncpy(&scratch[0], headp, SCRATCHSIZE); scratch[SCRATCHSIZE] = 0; /* null terminate */ res = Curl_convert_from_network(data, &scratch[0], @@ -3637,7 +3573,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* Curl_convert_from_network calls failf if unsuccessful */ return res; #else -#define HEADER1 k->p /* no conversion needed, just use k->p */ +#define HEADER1 headp /* no conversion needed, just use headp */ #endif /* CURL_DOES_CONVERSIONS */ if(conn->handler->protocol & PROTO_FAMILY_HTTP) { @@ -3649,6 +3585,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * guarantees on future behaviors since it isn't within the protocol. */ char separator; + char twoorthree[2]; nc = sscanf(HEADER1, " HTTP/%1d.%1d%c%3d", &httpversion_major, @@ -3656,8 +3593,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, &separator, &k->httpcode); - if(nc == 1 && httpversion_major == 2 && - 1 == sscanf(HEADER1, " HTTP/2 %d", &k->httpcode)) { + if(nc == 1 && httpversion_major >= 2 && + 2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) { conn->httpversion = 0; nc = 4; separator = ' '; @@ -3687,7 +3624,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, compare header line against list of aliases */ if(!nc) { - if(checkhttpprefix(data, k->p, k->hbuflen) == STATUS_DONE) { + statusline check = + checkhttpprefix(data, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); + if(check == STATUS_DONE) { nc = 1; k->httpcode = 200; conn->httpversion = 10; @@ -3695,7 +3636,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } else { - failf(data, "Unsupported HTTP version in response\n"); + failf(data, "Unsupported HTTP version in response"); return CURLE_UNSUPPORTED_PROTOCOL; } } @@ -3733,15 +3674,18 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * depending on how authentication is working. Other codes * are definitely errors, so give up here. */ - if(data->state.resume_from && data->set.httpreq == HTTPREQ_GET && + if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && k->httpcode == 416) { /* "Requested Range Not Satisfiable", just proceed and pretend this is no error */ k->ignorebody = TRUE; /* Avoid appending error msg to good data. */ } else if(data->set.http_fail_on_error && (k->httpcode >= 400) && - ((k->httpcode != 401) || !conn->bits.user_passwd) && - ((k->httpcode != 407) || !conn->bits.proxy_user_passwd) ) { + ((k->httpcode != 401) || !conn->bits.user_passwd) +#ifndef CURL_DISABLE_PROXY + && ((k->httpcode != 407) || !conn->bits.proxy_user_passwd) +#endif + ) { /* serious error, go home! */ print_http_error(data); return CURLE_HTTP_RETURNED_ERROR; @@ -3757,9 +3701,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, else if(conn->httpversion == 20 || (k->upgr101 == UPGR101_REQUESTED && k->httpcode == 101)) { DEBUGF(infof(data, "HTTP/2 found, allow multiplexing\n")); - - /* HTTP/2 cannot blacklist multiplexing since it is a core - functionality of the protocol */ + /* HTTP/2 cannot avoid multiplexing since it is a core functionality + of the protocol */ conn->bundle->multiuse = BUNDLE_MULTIPLEX; } else if(conn->httpversion >= 11 && @@ -3769,6 +3712,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, "HTTP 1.1 or later with persistent connection\n")); } + k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200; switch(k->httpcode) { case 304: /* (quote from RFC2616, section 10.3.5): The 304 response @@ -3786,10 +3730,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * empty line after the header fields. */ k->size = 0; k->maxdownload = 0; - k->ignorecl = TRUE; /* ignore Content-Length headers */ + k->http_bodyless = TRUE; break; default: - /* nothing */ break; } } @@ -3799,16 +3742,16 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } - result = Curl_convert_from_network(data, k->p, strlen(k->p)); + result = Curl_convert_from_network(data, headp, strlen(headp)); /* Curl_convert_from_network calls failf if unsuccessful */ if(result) return result; /* Check for Content-Length: header lines to get size */ - if(!k->ignorecl && !data->set.ignorecl && - checkprefix("Content-Length:", k->p)) { + if(!k->http_bodyless && + !data->set.ignorecl && checkprefix("Content-Length:", headp)) { curl_off_t contentlength; - CURLofft offt = curlx_strtoofft(k->p + 15, NULL, 10, &contentlength); + CURLofft offt = curlx_strtoofft(headp + 15, NULL, 10, &contentlength); if(offt == CURL_OFFT_OK) { if(data->set.max_filesize && @@ -3839,8 +3782,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } /* check for Content-Type: header lines to get the MIME-type */ - else if(checkprefix("Content-Type:", k->p)) { - char *contenttype = Curl_copy_header_value(k->p); + else if(checkprefix("Content-Type:", headp)) { + char *contenttype = Curl_copy_header_value(headp); if(!contenttype) return CURLE_OUT_OF_MEMORY; if(!*contenttype) @@ -3851,10 +3794,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, data->info.contenttype = contenttype; } } +#ifndef CURL_DISABLE_PROXY else if((conn->httpversion == 10) && conn->bits.httpproxy && - Curl_compareheader(k->p, - "Proxy-Connection:", "keep-alive")) { + Curl_compareheader(headp, "Proxy-Connection:", "keep-alive")) { /* * When a HTTP/1.0 reply comes when using a proxy, the * 'Proxy-Connection: keep-alive' line tells us the @@ -3866,8 +3809,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } else if((conn->httpversion == 11) && conn->bits.httpproxy && - Curl_compareheader(k->p, - "Proxy-Connection:", "close")) { + Curl_compareheader(headp, "Proxy-Connection:", "close")) { /* * We get a HTTP/1.1 response from a proxy and it says it'll * close down after this transfer. @@ -3875,8 +3817,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, connclose(conn, "Proxy-Connection: asked to close after done"); infof(data, "HTTP/1.1 proxy connection set close!\n"); } +#endif else if((conn->httpversion == 10) && - Curl_compareheader(k->p, "Connection:", "keep-alive")) { + Curl_compareheader(headp, "Connection:", "keep-alive")) { /* * A HTTP/1.0 reply with the 'Connection: keep-alive' line * tells us the connection will be kept alive for our @@ -3886,7 +3829,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, connkeep(conn, "Connection keep-alive"); infof(data, "HTTP/1.0 connection set to keep alive!\n"); } - else if(Curl_compareheader(k->p, "Connection:", "close")) { + else if(Curl_compareheader(headp, "Connection:", "close")) { /* * [RFC 2616, section 8.1.2.1] * "Connection: close" is HTTP/1.1 language and means that @@ -3895,7 +3838,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, */ streamclose(conn, "Connection: close used"); } - else if(checkprefix("Transfer-Encoding:", k->p)) { + else if(!k->http_bodyless && checkprefix("Transfer-Encoding:", headp)) { /* One or more encodings. We check for chunked and/or a compression algorithm. */ /* @@ -3907,11 +3850,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * of chunks, and a chunk-data set to zero signals the * end-of-chunks. */ - result = Curl_build_unencoding_stack(conn, k->p + 18, TRUE); + result = Curl_build_unencoding_stack(conn, headp + 18, TRUE); if(result) return result; } - else if(checkprefix("Content-Encoding:", k->p) && + else if(!k->http_bodyless && checkprefix("Content-Encoding:", headp) && data->set.str[STRING_ENCODING]) { /* * Process Content-Encoding. Look for the values: identity, @@ -3920,11 +3863,24 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * 2616). zlib cannot handle compress. However, errors are * handled further down when the response body is processed */ - result = Curl_build_unencoding_stack(conn, k->p + 17, FALSE); + result = Curl_build_unencoding_stack(conn, headp + 17, FALSE); if(result) return result; } - else if(checkprefix("Content-Range:", k->p)) { + else if(checkprefix("Retry-After:", headp)) { + /* Retry-After = HTTP-date / delay-seconds */ + curl_off_t retry_after = 0; /* zero for unknown or "now" */ + time_t date = Curl_getdate_capped(&headp[12]); + if(-1 == date) { + /* not a date, try it as a decimal number */ + (void)curlx_strtoofft(&headp[12], NULL, 10, &retry_after); + } + else + /* convert date to number of seconds into the future */ + retry_after = date - time(NULL); + data->info.retry_after = retry_after; /* store it */ + } + else if(!k->http_bodyless && checkprefix("Content-Range:", headp)) { /* Content-Range: bytes [num]- Content-Range: bytes: [num]- Content-Range: [num]- @@ -3936,7 +3892,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, The forth means the requested range was unsatisfied. */ - char *ptr = k->p + 14; + char *ptr = headp + 14; /* Move forward until first digit or asterisk */ while(*ptr && !ISDIGIT(*ptr) && *ptr != '*') @@ -3954,37 +3910,35 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, data->state.resume_from = 0; /* get everything */ } #if !defined(CURL_DISABLE_COOKIES) - else if(data->cookies && - checkprefix("Set-Cookie:", k->p)) { + else if(data->cookies && data->state.cookie_engine && + checkprefix("Set-Cookie:", headp)) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); Curl_cookie_add(data, - data->cookies, TRUE, FALSE, k->p + 11, + data->cookies, TRUE, FALSE, headp + 11, /* If there is a custom-set Host: name, use it here, or else use real peer host name. */ - conn->allocptr.cookiehost? - conn->allocptr.cookiehost:conn->host.name, + data->state.aptr.cookiehost? + data->state.aptr.cookiehost:conn->host.name, data->state.up.path, (conn->handler->protocol&CURLPROTO_HTTPS)? TRUE:FALSE); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } #endif - else if(checkprefix("Last-Modified:", k->p) && + else if(!k->http_bodyless && checkprefix("Last-Modified:", headp) && (data->set.timecondition || data->set.get_filetime) ) { - time_t secs = time(NULL); - k->timeofdoc = curl_getdate(k->p + strlen("Last-Modified:"), - &secs); + k->timeofdoc = Curl_getdate_capped(headp + strlen("Last-Modified:")); if(data->set.get_filetime) data->info.filetime = k->timeofdoc; } - else if((checkprefix("WWW-Authenticate:", k->p) && + else if((checkprefix("WWW-Authenticate:", headp) && (401 == k->httpcode)) || - (checkprefix("Proxy-authenticate:", k->p) && + (checkprefix("Proxy-authenticate:", headp) && (407 == k->httpcode))) { bool proxy = (k->httpcode == 407) ? TRUE : FALSE; - char *auth = Curl_copy_header_value(k->p); + char *auth = Curl_copy_header_value(headp); if(!auth) return CURLE_OUT_OF_MEMORY; @@ -3995,27 +3949,28 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(result) return result; } - #ifdef USE_SPNEGO - else if(checkprefix("Persistent-Auth", k->p)) { +#ifdef USE_SPNEGO + else if(checkprefix("Persistent-Auth", headp)) { struct negotiatedata *negdata = &conn->negotiate; struct auth *authp = &data->state.authhost; if(authp->picked == CURLAUTH_NEGOTIATE) { - char *persistentauth = Curl_copy_header_value(k->p); + char *persistentauth = Curl_copy_header_value(headp); if(!persistentauth) return CURLE_OUT_OF_MEMORY; - negdata->noauthpersist = checkprefix("false", persistentauth); + negdata->noauthpersist = checkprefix("false", persistentauth)? + TRUE:FALSE; negdata->havenoauthpersist = TRUE; infof(data, "Negotiate: noauthpersist -> %d, header part: %s", negdata->noauthpersist, persistentauth); free(persistentauth); } } - #endif +#endif else if((k->httpcode >= 300 && k->httpcode < 400) && - checkprefix("Location:", k->p) && + checkprefix("Location:", headp) && !data->req.location) { /* this is the URL that the server advises us to use instead */ - char *location = Curl_copy_header_value(k->p); + char *location = Curl_copy_header_value(headp); if(!location) return CURLE_OUT_OF_MEMORY; if(!*location) @@ -4040,7 +3995,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } #ifdef USE_ALTSVC /* If enabled, the header is incoming and this is over HTTPS */ - else if(data->asi && checkprefix("Alt-Svc:", k->p) && + else if(data->asi && checkprefix("Alt-Svc:", headp) && ((conn->handler->flags & PROTOPT_SSL) || #ifdef CURLDEBUG /* allow debug builds to circumvent the HTTPS restriction */ @@ -4052,7 +4007,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* the ALPN of the current request */ enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; result = Curl_altsvc_parse(data, data->asi, - &k->p[ strlen("Alt-Svc:") ], + &headp[ strlen("Alt-Svc:") ], id, conn->host.name, curlx_uitous(conn->remote_port)); if(result) @@ -4060,7 +4015,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } #endif else if(conn->handler->protocol & CURLPROTO_RTSP) { - result = Curl_rtsp_parseheader(conn, k->p); + result = Curl_rtsp_parseheader(conn, headp); if(result) return result; } @@ -4074,18 +4029,18 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, writetype |= CLIENTWRITE_BODY; if(data->set.verbose) - Curl_debug(data, CURLINFO_HEADER_IN, k->p, (size_t)k->hbuflen); + Curl_debug(data, CURLINFO_HEADER_IN, headp, + Curl_dyn_len(&data->state.headerb)); - result = Curl_client_write(conn, writetype, k->p, k->hbuflen); + result = Curl_client_write(conn, writetype, headp, + Curl_dyn_len(&data->state.headerb)); if(result) return result; - data->info.header_size += (long)k->hbuflen; - data->req.headerbytecount += (long)k->hbuflen; + data->info.header_size += Curl_dyn_len(&data->state.headerb); + data->req.headerbytecount += Curl_dyn_len(&data->state.headerb); - /* reset hbufp pointer && hbuflen */ - k->hbufp = data->state.headerbuff; - k->hbuflen = 0; + Curl_dyn_reset(&data->state.headerb); } while(*k->str); /* header line within buffer */ diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index a59fe7af0e1..641bc0b93ab 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -44,57 +44,30 @@ char *Curl_copy_header_value(const char *header); char *Curl_checkProxyheaders(const struct connectdata *conn, const char *thisheader); -/* ------------------------------------------------------------------------- */ -/* - * The add_buffer series of functions are used to build one large memory chunk - * from repeated function invokes. Used so that the entire HTTP request can - * be sent in one go. - */ -struct Curl_send_buffer { - char *buffer; - size_t size_max; - size_t size_used; -}; -typedef struct Curl_send_buffer Curl_send_buffer; - -Curl_send_buffer *Curl_add_buffer_init(void); -void Curl_add_buffer_free(Curl_send_buffer **inp); -CURLcode Curl_add_bufferf(Curl_send_buffer **inp, const char *fmt, ...) - WARN_UNUSED_RESULT; -CURLcode Curl_add_buffer(Curl_send_buffer **inp, const void *inptr, - size_t size) WARN_UNUSED_RESULT; -CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, - struct connectdata *conn, - curl_off_t *bytes_written, - size_t included_body_bytes, - int socketindex); - -CURLcode Curl_add_timecondition(struct Curl_easy *data, - Curl_send_buffer *buf); +CURLcode Curl_buffer_send(struct dynbuf *in, + struct connectdata *conn, + curl_off_t *bytes_written, + size_t included_body_bytes, + int socketindex); + +CURLcode Curl_add_timecondition(const struct connectdata *conn, + struct dynbuf *buf); CURLcode Curl_add_custom_headers(struct connectdata *conn, bool is_connect, - Curl_send_buffer *req_buffer); + struct dynbuf *req_buffer); CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, - Curl_send_buffer *buffer, + struct dynbuf *buf, struct Curl_easy *handle); /* protocol-specific functions set up to be called by the main engine */ CURLcode Curl_http(struct connectdata *conn, bool *done); CURLcode Curl_http_done(struct connectdata *, CURLcode, bool premature); CURLcode Curl_http_connect(struct connectdata *conn, bool *done); -CURLcode Curl_http_setup_conn(struct connectdata *conn); - -/* The following functions are defined in http_chunks.c */ -void Curl_httpchunk_init(struct connectdata *conn); -CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap, - ssize_t length, ssize_t *wrote); /* These functions are in http.c */ -void Curl_http_auth_stage(struct Curl_easy *data, int stage); CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, const char *auth); CURLcode Curl_http_auth_act(struct connectdata *conn); -CURLcode Curl_http_perhapsrewind(struct connectdata *conn); /* If only the PICKNONE bit is set, there has been a round-trip and we selected to use no auth at all. Ie, we actively select no auth, as opposed @@ -124,11 +97,15 @@ CURLcode Curl_http_perhapsrewind(struct connectdata *conn); * */ #ifndef EXPECT_100_THRESHOLD -#define EXPECT_100_THRESHOLD 1024 +#define EXPECT_100_THRESHOLD (1024*1024) #endif #endif /* CURL_DISABLE_HTTP */ +#ifdef USE_NGHTTP3 +struct h3out; /* see ngtcp2 */ +#endif + /**************************************************************************** * HTTP unique setup ***************************************************************************/ @@ -158,9 +135,9 @@ struct HTTP { } sending; #ifndef CURL_DISABLE_HTTP - Curl_send_buffer *send_buffer; /* used if the request couldn't be sent in - one chunk, points to an allocated - send_buffer struct */ + struct dynbuf send_buffer; /* used if the request couldn't be sent in one + chunk, points to an allocated send_buffer + struct */ #endif #ifdef USE_NGHTTP2 /*********** for HTTP/2 we store stream-local data here *************/ @@ -168,26 +145,43 @@ struct HTTP { bool bodystarted; /* We store non-final and final response headers here, per-stream */ - Curl_send_buffer *header_recvbuf; + struct dynbuf header_recvbuf; size_t nread_header_recvbuf; /* number of bytes in header_recvbuf fed into upper layer */ - Curl_send_buffer *trailer_recvbuf; int status_code; /* HTTP status code */ const uint8_t *pausedata; /* pointer to data received in on_data_chunk */ size_t pauselen; /* the number of bytes left in data */ - bool closed; /* TRUE on HTTP2 stream close */ bool close_handled; /* TRUE if stream closure is handled by libcurl */ + + char **push_headers; /* allocated array */ + size_t push_headers_used; /* number of entries filled in */ + size_t push_headers_alloc; /* number of entries allocated */ +#endif +#if defined(USE_NGHTTP2) || defined(USE_NGHTTP3) + bool closed; /* TRUE on HTTP2 stream close */ char *mem; /* points to a buffer in memory to store received data */ size_t len; /* size of the buffer 'mem' points to */ size_t memlen; /* size of data copied to mem */ - +#endif +#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC) + /* fields used by both HTTP/2 and HTTP/3 */ const uint8_t *upload_mem; /* points to a buffer to read from */ size_t upload_len; /* size of the buffer 'upload_mem' points to */ curl_off_t upload_left; /* number of bytes left to upload */ +#endif - char **push_headers; /* allocated array */ - size_t push_headers_used; /* number of entries filled in */ - size_t push_headers_alloc; /* number of entries allocated */ +#ifdef ENABLE_QUIC + /*********** for HTTP/3 we store stream-local data here *************/ + int64_t stream3_id; /* stream we are interested in */ + bool firstheader; /* FALSE until headers arrive */ + bool firstbody; /* FALSE until body arrives */ + bool h3req; /* FALSE until request is issued */ + bool upload_done; +#endif +#ifdef USE_NGHTTP3 + size_t unacked_window; + struct h3out *h3out; /* per-stream buffers for upload */ + struct dynbuf overflow; /* excess data received during a single Curl_read */ #endif }; diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index 8e7bc217e66..6cf651f0c08 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -36,6 +36,7 @@ #include "connect.h" #include "strtoofft.h" #include "strdup.h" +#include "dynbuf.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -43,19 +44,11 @@ #define H2_BUFSIZE 32768 -#if (NGHTTP2_VERSION_NUM < 0x010000) +#if (NGHTTP2_VERSION_NUM < 0x010c00) #error too old nghttp2 version, upgrade! #endif -#if (NGHTTP2_VERSION_NUM > 0x010800) -#define NGHTTP2_HAS_HTTP2_STRERROR 1 -#endif - -#if (NGHTTP2_VERSION_NUM >= 0x010900) -/* nghttp2_session_callbacks_set_error_callback is present in nghttp2 1.9.0 or - later */ -#define NGHTTP2_HAS_ERROR_CALLBACK 1 -#else +#ifdef CURL_DISABLE_VERBOSE_STRINGS #define nghttp2_session_callbacks_set_error_callback(x,y) #endif @@ -63,12 +56,12 @@ #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 #endif -#define HTTP2_HUGE_WINDOW_SIZE (1 << 30) +#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */ #ifdef DEBUG_HTTP2 #define H2BUGF(x) x #else -#define H2BUGF(x) do { } WHILE_FALSE +#define H2BUGF(x) do { } while(0) #endif @@ -100,16 +93,11 @@ void Curl_http2_init_userset(struct UserDefined *set) } static int http2_perform_getsock(const struct connectdata *conn, - curl_socket_t *sock, /* points to - numsocks - number of - sockets */ - int numsocks) + curl_socket_t *sock) { const struct http_conn *c = &conn->proto.httpc; struct SingleRequest *k = &conn->data->req; int bitmap = GETSOCK_BLANK; - (void)numsocks; sock[0] = conn->sock[FIRSTSOCKET]; @@ -126,11 +114,9 @@ static int http2_perform_getsock(const struct connectdata *conn, } static int http2_getsock(struct connectdata *conn, - curl_socket_t *sock, /* points to numsocks - number of sockets */ - int numsocks) + curl_socket_t *socks) { - return http2_perform_getsock(conn, sock, numsocks); + return http2_perform_getsock(conn, socks); } /* @@ -139,8 +125,7 @@ static int http2_getsock(struct connectdata *conn, static void http2_stream_free(struct HTTP *http) { if(http) { - Curl_add_buffer_free(&http->header_recvbuf); - Curl_add_buffer_free(&http->trailer_recvbuf); + Curl_dyn_free(&http->header_recvbuf); for(; http->push_headers_used > 0; --http->push_headers_used) { free(http->push_headers[http->push_headers_used - 1]); } @@ -240,7 +225,7 @@ static unsigned int http2_conncheck(struct connectdata *check, if(checks_to_perform & CONNCHECK_KEEPALIVE) { struct curltime now = Curl_now(); - time_t elapsed = Curl_timediff(now, check->keepalive); + timediff_t elapsed = Curl_timediff(now, check->keepalive); if(elapsed > check->upkeep_interval_ms) { /* Perform an HTTP/2 PING */ @@ -269,24 +254,22 @@ static unsigned int http2_conncheck(struct connectdata *check, return ret_val; } -/* called from Curl_http_setup_conn */ +/* called from http_setup_conn */ void Curl_http2_setup_req(struct Curl_easy *data) { struct HTTP *http = data->req.protop; - - http->nread_header_recvbuf = 0; http->bodystarted = FALSE; http->status_code = -1; http->pausedata = NULL; http->pauselen = 0; http->closed = FALSE; http->close_handled = FALSE; - http->mem = data->state.buffer; - http->len = data->set.buffer_size; + http->mem = NULL; + http->len = 0; http->memlen = 0; } -/* called from Curl_http_setup_conn */ +/* called from http_setup_conn */ void Curl_http2_setup_conn(struct connectdata *conn) { conn->proto.httpc.settings.max_concurrent_streams = @@ -348,36 +331,7 @@ static const struct Curl_handler Curl_handler_http2_ssl = { int Curl_http2_ver(char *p, size_t len) { nghttp2_info *h2 = nghttp2_version(0); - return msnprintf(p, len, " nghttp2/%s", h2->version_str); -} - -/* HTTP/2 error code to name based on the Error Code Registry. -https://tools.ietf.org/html/rfc7540#page-77 -nghttp2_error_code enums are identical. -*/ -static const char *http2_strerror(uint32_t err) -{ -#ifndef NGHTTP2_HAS_HTTP2_STRERROR - const char *str[] = { - "NO_ERROR", /* 0x0 */ - "PROTOCOL_ERROR", /* 0x1 */ - "INTERNAL_ERROR", /* 0x2 */ - "FLOW_CONTROL_ERROR", /* 0x3 */ - "SETTINGS_TIMEOUT", /* 0x4 */ - "STREAM_CLOSED", /* 0x5 */ - "FRAME_SIZE_ERROR", /* 0x6 */ - "REFUSED_STREAM", /* 0x7 */ - "CANCEL", /* 0x8 */ - "COMPRESSION_ERROR", /* 0x9 */ - "CONNECT_ERROR", /* 0xA */ - "ENHANCE_YOUR_CALM", /* 0xB */ - "INADEQUATE_SECURITY", /* 0xC */ - "HTTP_1_1_REQUIRED" /* 0xD */ - }; - return (err < sizeof(str) / sizeof(str[0])) ? str[err] : "unknown"; -#else - return nghttp2_http2_strerror(err); -#endif + return msnprintf(p, len, "nghttp2/%s", h2->version_str); } /* @@ -503,26 +457,58 @@ static struct Curl_easy *duphandle(struct Curl_easy *data) /* setup the request struct */ struct HTTP *http = calloc(1, sizeof(struct HTTP)); if(!http) { - (void)Curl_close(second); - second = NULL; + (void)Curl_close(&second); } else { second->req.protop = http; - http->header_recvbuf = Curl_add_buffer_init(); - if(!http->header_recvbuf) { - free(http); - (void)Curl_close(second); - second = NULL; - } - else { - Curl_http2_setup_req(second); - second->state.stream_weight = data->state.stream_weight; - } + Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS); + Curl_http2_setup_req(second); + second->state.stream_weight = data->state.stream_weight; } } return second; } +static int set_transfer_url(struct Curl_easy *data, + struct curl_pushheaders *hp) +{ + const char *v; + CURLU *u = curl_url(); + CURLUcode uc; + char *url; + + v = curl_pushheader_byname(hp, ":scheme"); + if(v) { + uc = curl_url_set(u, CURLUPART_SCHEME, v, 0); + if(uc) + return 1; + } + + v = curl_pushheader_byname(hp, ":authority"); + if(v) { + uc = curl_url_set(u, CURLUPART_HOST, v, 0); + if(uc) + return 2; + } + + v = curl_pushheader_byname(hp, ":path"); + if(v) { + uc = curl_url_set(u, CURLUPART_PATH, v, 0); + if(uc) + return 3; + } + + uc = curl_url_get(u, CURLUPART_URL, &url, 0); + if(uc) + return 4; + curl_url_cleanup(u); + + if(data->change.url_alloc) + free(data->change.url); + data->change.url_alloc = TRUE; + data->change.url = url; + return 0; +} static int push_promise(struct Curl_easy *data, struct connectdata *conn, @@ -554,11 +540,15 @@ static int push_promise(struct Curl_easy *data, stream = data->req.protop; if(!stream) { failf(data, "Internal NULL stream!\n"); - (void)Curl_close(newhandle); + (void)Curl_close(&newhandle); rv = 1; goto fail; } + rv = set_transfer_url(newhandle, &heads); + if(rv) + goto fail; + Curl_set_in_callback(data, true); rv = data->multi->push_cb(data, newhandle, stream->push_headers_used, &heads, @@ -576,7 +566,7 @@ static int push_promise(struct Curl_easy *data, /* denied, kill off the new handle again */ http2_stream_free(newhandle->req.protop); newhandle->req.protop = NULL; - (void)Curl_close(newhandle); + (void)Curl_close(&newhandle); goto fail; } @@ -592,7 +582,7 @@ static int push_promise(struct Curl_easy *data, infof(data, "failed to add handle to multi\n"); http2_stream_free(newhandle->req.protop); newhandle->req.protop = NULL; - Curl_close(newhandle); + Curl_close(&newhandle); rv = 1; goto fail; } @@ -714,15 +704,17 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, stream->status_code = -1; } - result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2); + result = Curl_dyn_add(&stream->header_recvbuf, "\r\n"); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf; + left = Curl_dyn_len(&stream->header_recvbuf) - + stream->nread_header_recvbuf; ncopy = CURLMIN(stream->len, left); memcpy(&stream->mem[stream->memlen], - stream->header_recvbuf->buffer + stream->nread_header_recvbuf, + Curl_dyn_ptr(&stream->header_recvbuf) + + stream->nread_header_recvbuf, ncopy); stream->nread_header_recvbuf += ncopy; @@ -847,7 +839,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, return 0; } H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n", - http2_strerror(error_code), error_code, stream_id)); + nghttp2_strerror(error_code), error_code, stream_id)); stream = data_s->req.protop; if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -855,6 +847,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream->closed = TRUE; httpc = &conn->proto.httpc; drain_this(data_s, httpc); + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); httpc->error_code = error_code; /* remove the entry from the hash as the stream is now gone */ @@ -897,12 +890,6 @@ static int on_begin_headers(nghttp2_session *session, return 0; } - if(!stream->trailer_recvbuf) { - stream->trailer_recvbuf = Curl_add_buffer_init(); - if(!stream->trailer_recvbuf) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - } return 0; } @@ -974,7 +961,9 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(!check) /* no memory */ return NGHTTP2_ERR_CALLBACK_FAILURE; - if(!Curl_strcasecompare(check, (const char *)value)) { + if(!Curl_strcasecompare(check, (const char *)value) && + ((conn->remote_port != conn->given->defport) || + !Curl_strcasecompare(conn->host.name, (const char *)value))) { /* This is push is not for the same authority that was asked for in * the URL. RFC 7540 section 8.2 says: "A client MUST treat a * PUSH_PROMISE for which the server is not authoritative as a stream @@ -1016,26 +1005,19 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, } if(stream->bodystarted) { - /* This is trailer fields. */ - /* 4 is for ": " and "\r\n". */ - uint32_t n = (uint32_t)(namelen + valuelen + 4); - + /* This is a trailer */ + struct dynbuf trail; H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen, value)); - - result = Curl_add_buffer(&stream->trailer_recvbuf, &n, sizeof(n)); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_add_buffer(&stream->trailer_recvbuf, name, namelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_add_buffer(&stream->trailer_recvbuf, ": ", 2); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_add_buffer(&stream->trailer_recvbuf, value, valuelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_add_buffer(&stream->trailer_recvbuf, "\r\n\0", 3); + Curl_dyn_init(&trail, DYN_H2_TRAILER); + result = Curl_dyn_addf(&trail, + "%.*s: %.*s\r\n", namelen, name, + valuelen, value); + if(!result) + result = Curl_client_write(conn, CLIENTWRITE_HEADER, + Curl_dyn_ptr(&trail), + Curl_dyn_len(&trail)); + Curl_dyn_free(&trail); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1050,14 +1032,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, stream->status_code = decode_status_code(value, valuelen); DEBUGASSERT(stream->status_code != -1); - result = Curl_add_buffer(&stream->header_recvbuf, "HTTP/2 ", 7); + result = Curl_dyn_add(&stream->header_recvbuf, "HTTP/2 "); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen); + result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* the space character after the status code is mandatory */ - result = Curl_add_buffer(&stream->header_recvbuf, " \r\n", 3); + result = Curl_dyn_add(&stream->header_recvbuf, " \r\n"); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ @@ -1072,16 +1054,16 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, /* nghttp2 guarantees that namelen > 0, and :status was already received, and this is not pseudo-header field . */ /* convert to a HTTP1-style header */ - result = Curl_add_buffer(&stream->header_recvbuf, name, namelen); + result = Curl_dyn_addn(&stream->header_recvbuf, name, namelen); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_add_buffer(&stream->header_recvbuf, ": ", 2); + result = Curl_dyn_add(&stream->header_recvbuf, ": "); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen); + result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2); + result = Curl_dyn_add(&stream->header_recvbuf, "\r\n"); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ @@ -1144,8 +1126,7 @@ static ssize_t data_source_read_callback(nghttp2_session *session, return nread; } -#if defined(NGHTTP2_HAS_ERROR_CALLBACK) && \ - !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) static int error_callback(nghttp2_session *session, const char *msg, size_t len, @@ -1162,9 +1143,10 @@ static void populate_settings(struct connectdata *conn, struct http_conn *httpc) { nghttp2_settings_entry *iv = httpc->local_settings; + DEBUGASSERT(conn->data); iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = 100; + iv[0].value = Curl_multi_max_concurrent_streams(conn->data->multi); iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = HTTP2_HUGE_WINDOW_SIZE; @@ -1175,33 +1157,26 @@ static void populate_settings(struct connectdata *conn, httpc->local_settings_num = 3; } -void Curl_http2_done(struct connectdata *conn, bool premature) +void Curl_http2_done(struct Curl_easy *data, bool premature) { - struct Curl_easy *data = conn->data; struct HTTP *http = data->req.protop; - struct http_conn *httpc = &conn->proto.httpc; + struct http_conn *httpc = &data->conn->proto.httpc; /* there might be allocated resources done before this got the 'h2' pointer setup */ - if(http->header_recvbuf) { - Curl_add_buffer_free(&http->header_recvbuf); - Curl_add_buffer_free(&http->trailer_recvbuf); - if(http->push_headers) { - /* if they weren't used and then freed before */ - for(; http->push_headers_used > 0; --http->push_headers_used) { - free(http->push_headers[http->push_headers_used - 1]); - } - free(http->push_headers); - http->push_headers = NULL; + Curl_dyn_free(&http->header_recvbuf); + if(http->push_headers) { + /* if they weren't used and then freed before */ + for(; http->push_headers_used > 0; --http->push_headers_used) { + free(http->push_headers[http->push_headers_used - 1]); } + free(http->push_headers); + http->push_headers = NULL; } if(!httpc->h2) /* not HTTP/2 ? */ return; - if(data->state.drain) - drained_transfer(data, httpc); - if(premature) { /* RST_STREAM */ if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, @@ -1213,6 +1188,10 @@ void Curl_http2_done(struct connectdata *conn, bool premature) httpc->pause_stream_id = 0; } } + + if(data->state.drain) + drained_transfer(data, httpc); + /* -1 means unassigned and 0 means cleared */ if(http->stream_id > 0) { int rv = nghttp2_session_set_stream_user_data(httpc->h2, @@ -1263,9 +1242,7 @@ static CURLcode http2_init(struct connectdata *conn) /* nghttp2_on_header_callback */ nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header); -#ifndef CURL_DISABLE_VERBOSE_STRINGS nghttp2_session_callbacks_set_error_callback(callbacks, error_callback); -#endif /* The nghttp2 session is not yet setup, do it */ rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn); @@ -1283,7 +1260,7 @@ static CURLcode http2_init(struct connectdata *conn) /* * Append headers to ask for a HTTP1.1 to HTTP2 upgrade. */ -CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, +CURLcode Curl_http2_request_upgrade(struct dynbuf *req, struct connectdata *conn) { CURLcode result; @@ -1302,7 +1279,7 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, httpc->local_settings_num); if(!binlen) { failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload"); - Curl_add_buffer_free(&req); + Curl_dyn_free(req); return CURLE_FAILED_INIT; } conn->proto.httpc.binlen = binlen; @@ -1310,15 +1287,15 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen, &base64, &blen); if(result) { - Curl_add_buffer_free(&req); + Curl_dyn_free(req); return result; } - result = Curl_add_bufferf(&req, - "Connection: Upgrade, HTTP2-Settings\r\n" - "Upgrade: %s\r\n" - "HTTP2-Settings: %s\r\n", - NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); + result = Curl_dyn_addf(req, + "Connection: Upgrade, HTTP2-Settings\r\n" + "Upgrade: %s\r\n" + "HTTP2-Settings: %s\r\n", + NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); free(base64); k->upgr101 = UPGR101_REQUESTED; @@ -1412,10 +1389,11 @@ CURLcode Curl_http2_done_sending(struct connectdata *conn) struct HTTP *stream = conn->data->req.protop; + struct http_conn *httpc = &conn->proto.httpc; + nghttp2_session *h2 = httpc->h2; + if(stream->upload_left) { /* If the stream still thinks there's data left to upload. */ - struct http_conn *httpc = &conn->proto.httpc; - nghttp2_session *h2 = httpc->h2; stream->upload_left = 0; /* DONE! */ @@ -1425,6 +1403,23 @@ CURLcode Curl_http2_done_sending(struct connectdata *conn) (void)h2_process_pending_input(conn, httpc, &result); } + + /* If nghttp2 still has pending frames unsent */ + if(nghttp2_session_want_write(h2)) { + struct Curl_easy *data = conn->data; + struct SingleRequest *k = &data->req; + int rv; + + H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)\n", data)); + + /* re-set KEEP_SEND to make sure we are called again */ + k->keepon |= KEEP_SEND; + + /* and attempt to send the pending frames */ + rv = h2_session_send(data, h2); + if(rv != 0) + result = CURLE_SEND_ERROR; + } } return result; } @@ -1433,8 +1428,6 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, struct Curl_easy *data, struct HTTP *stream, CURLcode *err) { - char *trailer_pos, *trailer_end; - CURLcode result; struct http_conn *httpc = &conn->proto.httpc; if(httpc->pause_stream_id == stream->stream_id) { @@ -1463,7 +1456,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, } else if(httpc->error_code != NGHTTP2_NO_ERROR) { failf(data, "HTTP/2 stream %d was not closed cleanly: %s (err %u)", - stream->stream_id, http2_strerror(httpc->error_code), + stream->stream_id, nghttp2_strerror(httpc->error_code), httpc->error_code); *err = CURLE_HTTP2_STREAM; return -1; @@ -1477,25 +1470,6 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, return -1; } - if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) { - trailer_pos = stream->trailer_recvbuf->buffer; - trailer_end = trailer_pos + stream->trailer_recvbuf->size_used; - - for(; trailer_pos < trailer_end;) { - uint32_t n; - memcpy(&n, trailer_pos, sizeof(n)); - trailer_pos += sizeof(n); - - result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n); - if(result) { - *err = result; - return -1; - } - - trailer_pos += n + 1; - } - } - stream->close_handled = TRUE; H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n")); @@ -1541,6 +1515,7 @@ static int h2_session_send(struct Curl_easy *data, H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n", stream->stream_id, data)); + DEBUGASSERT(stream->stream_id != -1); rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id, &pri_spec); if(rv) @@ -1565,6 +1540,11 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, if(should_close_session(httpc)) { H2BUGF(infof(data, "http2_recv: nothing to do in this session\n")); + if(conn->bits.close) { + /* already marked for closure, return OK and we're done */ + *err = CURLE_OK; + return 0; + } *err = CURLE_HTTP2; return -1; } @@ -1580,13 +1560,13 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, */ if(stream->bodystarted && - stream->nread_header_recvbuf < stream->header_recvbuf->size_used) { - /* If there is body data pending for this stream to return, do that */ + stream->nread_header_recvbuf < Curl_dyn_len(&stream->header_recvbuf)) { + /* If there is header data pending for this stream to return, do that */ size_t left = - stream->header_recvbuf->size_used - stream->nread_header_recvbuf; + Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf; size_t ncopy = CURLMIN(len, left); - memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf, - ncopy); + memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) + + stream->nread_header_recvbuf, ncopy); stream->nread_header_recvbuf += ncopy; H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n", @@ -1594,8 +1574,12 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, return ncopy; } - H2BUGF(infof(data, "http2_recv: easy %p (stream %u)\n", - data, stream->stream_id)); + H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u\n", + data, stream->stream_id, + nghttp2_session_get_local_window_size(httpc->h2), + nghttp2_session_get_stream_local_window_size(httpc->h2, + stream->stream_id) + )); if((data->state.drain) && stream->memlen) { H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n", @@ -1626,7 +1610,6 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, stream->pausedata += nread; stream->pauselen -= nread; - infof(data, "%zd data bytes written\n", nread); if(stream->pauselen == 0) { H2BUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id)); DEBUGASSERT(httpc->pause_stream_id == stream->stream_id); @@ -1660,6 +1643,9 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, socket is not read. But it seems that usually streams are notified with its drain property, and socket is read again quickly. */ + if(stream->closed) + /* closed overrides paused */ + return 0; H2BUGF(infof(data, "stream %x is paused, pause id: %x\n", stream->stream_id, httpc->pause_stream_id)); *err = CURLE_AGAIN; @@ -1754,14 +1740,14 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, else if(!stream->closed) { drained_transfer(data, httpc); } + else + /* this stream is closed, trigger a another read ASAP to detect that */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); return retlen; } - /* If stream is closed, return 0 to signal the http routine to close - the connection */ - if(stream->closed) { - return http2_handle_stream_close(conn, data, stream, err); - } + if(stream->closed) + return 0; *err = CURLE_AGAIN; H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u\n", stream->stream_id)); @@ -1772,8 +1758,9 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, field list. */ #define AUTHORITY_DST_IDX 3 +/* USHRT_MAX is 65535 == 0xffff */ #define HEADER_OVERFLOW(x) \ - (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen) + (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen) /* * Check header memory for the token "trailers". @@ -1880,7 +1867,11 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, are going to send or sending request body in DATA frame */ stream->upload_mem = mem; stream->upload_len = len; - nghttp2_session_resume_data(h2, stream->stream_id); + rv = nghttp2_session_resume_data(h2, stream->stream_id); + if(nghttp2_is_fatal(rv)) { + *err = CURLE_SEND_ERROR; + return -1; + } rv = h2_session_send(conn->data, h2); if(nghttp2_is_fatal(rv)) { *err = CURLE_SEND_ERROR; @@ -2019,8 +2010,10 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, nva[i].namelen = strlen((char *)nva[i].name); } else { - nva[i].name = (unsigned char *)hdbuf; nva[i].namelen = (size_t)(end - hdbuf); + /* Lower case the header name for HTTP/2 */ + Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen); + nva[i].name = (unsigned char *)hdbuf; } hdbuf = end + 1; while(*hdbuf == ' ' || *hdbuf == '\t') @@ -2082,7 +2075,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, h2_pri_spec(conn->data, &pri_spec); - switch(conn->data->set.httpreq) { + switch(conn->data->state.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: case HTTPREQ_POST_MIME: @@ -2130,17 +2123,14 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, return -1; } - if(stream->stream_id != -1) { - /* If whole HEADERS frame was sent off to the underlying socket, - the nghttp2 library calls data_source_read_callback. But only - it found that no data available, so it deferred the DATA - transmission. Which means that nghttp2_session_want_write() - returns 0 on http2_perform_getsock(), which results that no - writable socket check is performed. To workaround this, we - issue nghttp2_session_resume_data() here to bring back DATA - transmission from deferred state. */ - nghttp2_session_resume_data(h2, stream->stream_id); - } + /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2 + library calls data_source_read_callback. But only it found that no data + available, so it deferred the DATA transmission. Which means that + nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which + results that no writable socket check is performed. To workaround this, + we issue nghttp2_session_resume_data() here to bring back DATA + transmission from deferred state. */ + nghttp2_session_resume_data(h2, stream->stream_id); return len; @@ -2156,13 +2146,11 @@ CURLcode Curl_http2_setup(struct connectdata *conn) struct http_conn *httpc = &conn->proto.httpc; struct HTTP *stream = conn->data->req.protop; + DEBUGASSERT(conn->data->state.buffer); + stream->stream_id = -1; - if(!stream->header_recvbuf) { - stream->header_recvbuf = Curl_add_buffer_init(); - if(!stream->header_recvbuf) - return CURLE_OUT_OF_MEMORY; - } + Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS); if((conn->handler == &Curl_handler_http2_ssl) || (conn->handler == &Curl_handler_http2)) @@ -2175,7 +2163,7 @@ CURLcode Curl_http2_setup(struct connectdata *conn) result = http2_init(conn); if(result) { - Curl_add_buffer_free(&stream->header_recvbuf); + Curl_dyn_free(&stream->header_recvbuf); return result; } @@ -2183,6 +2171,8 @@ CURLcode Curl_http2_setup(struct connectdata *conn) stream->upload_left = 0; stream->upload_mem = NULL; stream->upload_len = 0; + stream->mem = conn->data->state.buffer; + stream->len = conn->data->set.buffer_size; httpc->inbuflen = 0; httpc->nread_inbuf = 0; @@ -2255,7 +2245,6 @@ CURLcode Curl_http2_switched(struct connectdata *conn, } } -#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0, HTTP2_HUGE_WINDOW_SIZE); if(rv != 0) { @@ -2263,7 +2252,6 @@ CURLcode Curl_http2_switched(struct connectdata *conn, nghttp2_strerror(rv), rv); return CURLE_HTTP2; } -#endif /* we are going to copy mem to httpc->inbuf. This is required since mem is part of buffer pointed by stream->mem, and callbacks @@ -2321,6 +2309,51 @@ CURLcode Curl_http2_switched(struct connectdata *conn, return CURLE_OK; } +CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + /* if it isn't HTTP/2, we're done */ + if(!data->conn->proto.httpc.h2) + return CURLE_OK; +#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE + else { + struct HTTP *stream = data->req.protop; + struct http_conn *httpc = &data->conn->proto.httpc; + uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE; + int rv = nghttp2_session_set_local_window_size(httpc->h2, + NGHTTP2_FLAG_NONE, + stream->stream_id, + window); + if(rv) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rv), rv); + return CURLE_HTTP2; + } + + /* make sure the window update gets sent */ + rv = h2_session_send(data, httpc->h2); + if(rv) + return CURLE_SEND_ERROR; + + DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u\n", + window, stream->stream_id)); + +#ifdef DEBUGBUILD + { + /* read out the stream local window again */ + uint32_t window2 = + nghttp2_session_get_stream_local_window_size(httpc->h2, + stream->stream_id); + DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u\n", + window2, stream->stream_id)); + } +#endif + } +#endif + return CURLE_OK; +} + CURLcode Curl_http2_add_child(struct Curl_easy *parent, struct Curl_easy *child, bool exclusive) @@ -2414,8 +2447,6 @@ bool Curl_h2_http_1_1_error(struct connectdata *conn) #else /* !USE_NGHTTP2 */ /* Satisfy external references even if http2 is not compiled in. */ - -#define CURL_DISABLE_TYPECHECK #include char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h index db6217b11e4..e82b212809a 100644 --- a/Utilities/cmcurl/lib/http2.h +++ b/Utilities/cmcurl/lib/http2.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -42,16 +42,15 @@ const char *Curl_http2_strerror(uint32_t err); CURLcode Curl_http2_init(struct connectdata *conn); void Curl_http2_init_state(struct UrlState *state); void Curl_http2_init_userset(struct UserDefined *set); -CURLcode Curl_http2_send_request(struct connectdata *conn); -CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, +CURLcode Curl_http2_request_upgrade(struct dynbuf *req, struct connectdata *conn); CURLcode Curl_http2_setup(struct connectdata *conn); CURLcode Curl_http2_switched(struct connectdata *conn, const char *data, size_t nread); -/* called from Curl_http_setup_conn */ +/* called from http_setup_conn */ void Curl_http2_setup_conn(struct connectdata *conn); void Curl_http2_setup_req(struct Curl_easy *data); -void Curl_http2_done(struct connectdata *conn, bool premature); +void Curl_http2_done(struct Curl_easy *data, bool premature); CURLcode Curl_http2_done_sending(struct connectdata *conn); CURLcode Curl_http2_add_child(struct Curl_easy *parent, struct Curl_easy *child, @@ -59,11 +58,11 @@ CURLcode Curl_http2_add_child(struct Curl_easy *parent, void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child); void Curl_http2_cleanup_dependencies(struct Curl_easy *data); +CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause); /* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */ bool Curl_h2_http_1_1_error(struct connectdata *conn); #else /* USE_NGHTTP2 */ -#define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_setup(x) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL @@ -76,6 +75,7 @@ bool Curl_h2_http_1_1_error(struct connectdata *conn); #define Curl_http2_add_child(x, y, z) #define Curl_http2_remove_child(x, y) #define Curl_http2_cleanup_dependencies(x) +#define Curl_http2_stream_pause(x, y) #define Curl_h2_http_1_1_error(x) 0 #endif diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index 18dfcb28240..767f806c81b 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,7 +26,7 @@ #include "urldata.h" /* it includes http_chunks.h */ #include "sendf.h" /* for the client write stuff */ - +#include "dynbuf.h" #include "content_encoding.h" #include "http.h" #include "non-ascii.h" /* for Curl_convert_to_network prototype */ @@ -93,6 +93,7 @@ void Curl_httpchunk_init(struct connectdata *conn) chunk->hexindex = 0; /* start at 0 */ chunk->dataleft = 0; /* no data left yet! */ chunk->state = CHUNK_HEX; /* we get hex first! */ + Curl_dyn_init(&conn->trailer, DYN_H1_TRAILER); } /* @@ -109,7 +110,8 @@ void Curl_httpchunk_init(struct connectdata *conn) CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap, ssize_t datalen, - ssize_t *wrotep) + ssize_t *wrotep, + CURLcode *extrap) { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; @@ -125,8 +127,10 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, chunk read process, to properly calculate the content length*/ if(data->set.http_te_skip && !k->ignorebody) { result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, datalen); - if(result) - return CHUNKE_WRITE_ERROR; + if(result) { + *extrap = result; + return CHUNKE_PASSTHRU_ERROR; + } } while(length) { @@ -174,7 +178,6 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, /* we're now expecting data to come, unless size was zero! */ if(0 == ch->datasize) { ch->state = CHUNK_TRAILER; /* now check for trailers */ - conn->trlPos = 0; } else ch->state = CHUNK_DATA; @@ -197,8 +200,10 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, else result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, piece); - if(result) - return CHUNKE_WRITE_ERROR; + if(result) { + *extrap = result; + return CHUNKE_PASSTHRU_ERROR; + } } *wrote += piece; @@ -224,30 +229,33 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, case CHUNK_TRAILER: if((*datap == 0x0d) || (*datap == 0x0a)) { + char *tr = Curl_dyn_ptr(&conn->trailer); /* this is the end of a trailer, but if the trailer was zero bytes there was no trailer and we move on */ - if(conn->trlPos) { - /* we allocate trailer with 3 bytes extra room to fit this */ - conn->trailer[conn->trlPos++] = 0x0d; - conn->trailer[conn->trlPos++] = 0x0a; - conn->trailer[conn->trlPos] = 0; + if(tr) { + size_t trlen; + result = Curl_dyn_add(&conn->trailer, (char *)"\x0d\x0a"); + if(result) + return CHUNKE_OUT_OF_MEMORY; + tr = Curl_dyn_ptr(&conn->trailer); + trlen = Curl_dyn_len(&conn->trailer); /* Convert to host encoding before calling Curl_client_write */ - result = Curl_convert_from_network(conn->data, conn->trailer, - conn->trlPos); + result = Curl_convert_from_network(conn->data, tr, trlen); if(result) /* Curl_convert_from_network calls failf if unsuccessful */ /* Treat it as a bad chunk */ return CHUNKE_BAD_CHUNK; if(!data->set.http_te_skip) { - result = Curl_client_write(conn, CLIENTWRITE_HEADER, - conn->trailer, conn->trlPos); - if(result) - return CHUNKE_WRITE_ERROR; + result = Curl_client_write(conn, CLIENTWRITE_HEADER, tr, trlen); + if(result) { + *extrap = result; + return CHUNKE_PASSTHRU_ERROR; + } } - conn->trlPos = 0; + Curl_dyn_reset(&conn->trailer); ch->state = CHUNK_TRAILER_CR; if(*datap == 0x0a) /* already on the LF */ @@ -260,25 +268,9 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, } } else { - /* conn->trailer is assumed to be freed in url.c on a - connection basis */ - if(conn->trlPos >= conn->trlMax) { - /* we always allocate three extra bytes, just because when the full - header has been received we append CRLF\0 */ - char *ptr; - if(conn->trlMax) { - conn->trlMax *= 2; - ptr = realloc(conn->trailer, conn->trlMax + 3); - } - else { - conn->trlMax = 128; - ptr = malloc(conn->trlMax + 3); - } - if(!ptr) - return CHUNKE_OUT_OF_MEMORY; - conn->trailer = ptr; - } - conn->trailer[conn->trlPos++]=*datap; + result = Curl_dyn_addn(&conn->trailer, datap, 1); + if(result) + return CHUNKE_OUT_OF_MEMORY; } datap++; length--; @@ -339,8 +331,9 @@ const char *Curl_chunked_strerror(CHUNKcode code) return "Illegal or missing hexadecimal sequence"; case CHUNKE_BAD_CHUNK: return "Malformed encoding found"; - case CHUNKE_WRITE_ERROR: - return "Write error"; + case CHUNKE_PASSTHRU_ERROR: + DEBUGASSERT(0); /* never used */ + return ""; case CHUNKE_BAD_ENCODING: return "Bad content-encoding found"; case CHUNKE_OUT_OF_MEMORY: diff --git a/Utilities/cmcurl/lib/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h index b969c5590a9..8f4a33c8e4c 100644 --- a/Utilities/cmcurl/lib/http_chunks.h +++ b/Utilities/cmcurl/lib/http_chunks.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -21,6 +21,9 @@ * KIND, either express or implied. * ***************************************************************************/ + +struct connectdata; + /* * The longest possible hexadecimal number we support in a chunked transfer. * Weird enough, RFC2616 doesn't set a maximum size! Since we use strtoul() @@ -71,9 +74,9 @@ typedef enum { CHUNKE_TOO_LONG_HEX = 1, CHUNKE_ILLEGAL_HEX, CHUNKE_BAD_CHUNK, - CHUNKE_WRITE_ERROR, CHUNKE_BAD_ENCODING, CHUNKE_OUT_OF_MEMORY, + CHUNKE_PASSTHRU_ERROR, /* Curl_httpchunk_read() returns a CURLcode to use */ CHUNKE_LAST } CHUNKcode; @@ -87,4 +90,10 @@ struct Curl_chunker { size_t dataleft; /* untouched data amount at the end of the last buffer */ }; +/* The following functions are defined in http_chunks.c */ +void Curl_httpchunk_init(struct connectdata *conn); +CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap, + ssize_t length, ssize_t *wrote, + CURLcode *passthru); + #endif /* HEADER_CURL_HTTP_CHUNKS_H */ diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c index 9616c30ed41..b06dc0d825a 100644 --- a/Utilities/cmcurl/lib/http_digest.c +++ b/Utilities/cmcurl/lib/http_digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -94,15 +94,19 @@ CURLcode Curl_output_digest(struct connectdata *conn, struct auth *authp; if(proxy) { +#ifdef CURL_DISABLE_PROXY + return CURLE_NOT_BUILT_IN; +#else digest = &data->state.proxydigest; - allocuserpwd = &conn->allocptr.proxyuserpwd; + allocuserpwd = &data->state.aptr.proxyuserpwd; userp = conn->http_proxy.user; passwdp = conn->http_proxy.passwd; authp = &data->state.authproxy; +#endif } else { digest = &data->state.digest; - allocuserpwd = &conn->allocptr.userpwd; + allocuserpwd = &data->state.aptr.userpwd; userp = conn->user; passwdp = conn->passwd; authp = &data->state.authhost; diff --git a/Utilities/cmcurl/lib/http_digest.h b/Utilities/cmcurl/lib/http_digest.h index 73410ae88e2..96e39a7dccd 100644 --- a/Utilities/cmcurl/lib/http_digest.h +++ b/Utilities/cmcurl/lib/http_digest.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c index c8f40644492..0a19ec2afab 100644 --- a/Utilities/cmcurl/lib/http_negotiate.c +++ b/Utilities/cmcurl/lib/http_negotiate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -52,6 +52,7 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, curlnegotiate state; if(proxy) { +#ifndef CURL_DISABLE_PROXY userp = conn->http_proxy.user; passwdp = conn->http_proxy.passwd; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? @@ -59,6 +60,9 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, host = conn->http_proxy.host.name; neg_ctx = &conn->proxyneg; state = conn->proxy_negotiate_state; +#else + return CURLE_NOT_BUILT_IN; +#endif } else { userp = conn->user; @@ -119,7 +123,8 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) struct auth *authp = proxy ? &conn->data->state.authproxy : &conn->data->state.authhost; curlnegotiate *state = proxy ? &conn->proxy_negotiate_state : - &conn->http_negotiate_state; + &conn->http_negotiate_state; + struct Curl_easy *data = conn->data; char *base64 = NULL; size_t len = 0; char *userp; @@ -148,10 +153,10 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) } if(!neg_ctx->context) { result = Curl_input_negotiate(conn, proxy, "Negotiate"); - if(result == CURLE_LOGIN_DENIED) { + if(result == CURLE_AUTH_ERROR) { /* negotiate auth failed, let's continue unauthenticated to stay * compatible with the behavior before curl-7_64_0-158-g6c6035532 */ - conn->data->state.authproblem = TRUE; + authp->done = TRUE; return CURLE_OK; } else if(result) @@ -164,15 +169,15 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) return result; userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", - base64); + base64); if(proxy) { - Curl_safefree(conn->allocptr.proxyuserpwd); - conn->allocptr.proxyuserpwd = userp; + Curl_safefree(data->state.aptr.proxyuserpwd); + data->state.aptr.proxyuserpwd = userp; } else { - Curl_safefree(conn->allocptr.userpwd); - conn->allocptr.userpwd = userp; + Curl_safefree(data->state.aptr.userpwd); + data->state.aptr.userpwd = userp; } free(base64); diff --git a/Utilities/cmcurl/lib/http_negotiate.h b/Utilities/cmcurl/lib/http_negotiate.h index 4f0ac1686a2..a737f6f781d 100644 --- a/Utilities/cmcurl/lib/http_negotiate.h +++ b/Utilities/cmcurl/lib/http_negotiate.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -33,6 +33,8 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy); void Curl_http_auth_cleanup_negotiate(struct connectdata *conn); -#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */ +#else /* !CURL_DISABLE_HTTP && USE_SPNEGO */ +#define Curl_http_auth_cleanup_negotiate(x) +#endif #endif /* HEADER_CURL_HTTP_NEGOTIATE_H */ diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c index e4a4fe05d08..cab543c757d 100644 --- a/Utilities/cmcurl/lib/http_ntlm.c +++ b/Utilities/cmcurl/lib/http_ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -44,9 +44,7 @@ /* SSL backend-specific #if branches in this file must be kept in the order documented in curl_ntlm_core. */ -#if defined(NTLM_NEEDS_NSS_INIT) -#include "vtls/nssg.h" -#elif defined(USE_WINDOWS_SSPI) +#if defined(USE_WINDOWS_SSPI) #include "curl_sspi.h" #endif @@ -133,17 +131,15 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy) struct ntlmdata *ntlm; curlntlm *state; struct auth *authp; + struct Curl_easy *data = conn->data; - DEBUGASSERT(conn); - DEBUGASSERT(conn->data); -#if defined(NTLM_NEEDS_NSS_INIT) - if(CURLE_OK != Curl_nss_force_init(conn->data)) - return CURLE_OUT_OF_MEMORY; -#endif + DEBUGASSERT(conn); + DEBUGASSERT(data); if(proxy) { - allocuserpwd = &conn->allocptr.proxyuserpwd; +#ifndef CURL_DISABLE_PROXY + allocuserpwd = &data->state.aptr.proxyuserpwd; userp = conn->http_proxy.user; passwdp = conn->http_proxy.passwd; service = conn->data->set.str[STRING_PROXY_SERVICE_NAME] ? @@ -152,9 +148,12 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy) ntlm = &conn->proxyntlm; state = &conn->proxy_ntlm_state; authp = &conn->data->state.authproxy; +#else + return CURLE_NOT_BUILT_IN; +#endif } else { - allocuserpwd = &conn->allocptr.userpwd; + allocuserpwd = &data->state.aptr.userpwd; userp = conn->user; passwdp = conn->passwd; service = conn->data->set.str[STRING_SERVICE_NAME] ? diff --git a/Utilities/cmcurl/lib/http_ntlm.h b/Utilities/cmcurl/lib/http_ntlm.h index 003714dbdea..3ebdf979e4f 100644 --- a/Utilities/cmcurl/lib/http_ntlm.h +++ b/Utilities/cmcurl/lib/http_ntlm.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,6 +35,8 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy); void Curl_http_auth_cleanup_ntlm(struct connectdata *conn); -#endif /* !CURL_DISABLE_HTTP && USE_NTLM */ +#else /* !CURL_DISABLE_HTTP && USE_NTLM */ +#define Curl_http_auth_cleanup_ntlm(x) +#endif #endif /* HEADER_CURL_HTTP_NTLM_H */ diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index d7ed11761fa..f188cbfc608 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -58,8 +58,9 @@ static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex) Curl_ssl_connect_nonblocking(conn, sockindex, &conn->bits.proxy_ssl_connected[sockindex]); if(result) - conn->bits.close = TRUE; /* a failed connection is marked for closure to - prevent (bad) re-use or similar */ + /* a failed connection is marked for closure to prevent (bad) re-use or + similar */ + connclose(conn, "TLS handshake failed"); } return result; #else @@ -71,6 +72,7 @@ static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex) CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex) { + struct Curl_easy *data = conn->data; if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { const CURLcode result = https_proxy_connect(conn, sockindex); if(result) @@ -126,7 +128,7 @@ CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex) conn->data->req.protop = prot_save; if(CURLE_OK != result) return result; - Curl_safefree(conn->allocptr.proxyuserpwd); + Curl_safefree(data->state.aptr.proxyuserpwd); #else return CURLE_NOT_BUILT_IN; #endif @@ -157,15 +159,15 @@ static CURLcode connect_init(struct connectdata *conn, bool reinit) return CURLE_OUT_OF_MEMORY; infof(conn->data, "allocate connect buffer!\n"); conn->connect_state = s; + Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS); } else { DEBUGASSERT(conn->connect_state); s = conn->connect_state; + Curl_dyn_reset(&s->rcvbuf); } s->tunnel_state = TUNNEL_INIT; s->keepon = TRUE; - s->line_start = s->connect_buffer; - s->ptr = s->line_start; s->cl = 0; s->close_connection = FALSE; return CURLE_OK; @@ -175,6 +177,7 @@ static void connect_done(struct connectdata *conn) { struct http_connect_state *s = conn->connect_state; s->tunnel_state = TUNNEL_COMPLETE; + Curl_dyn_free(&s->rcvbuf); infof(conn->data, "CONNECT phase completed!\n"); } @@ -189,6 +192,8 @@ static CURLcode CONNECT(struct connectdata *conn, CURLcode result; curl_socket_t tunnelsocket = conn->sock[sockindex]; struct http_connect_state *s = conn->connect_state; + char *linep; + size_t perline; #define SELECT_OK 0 #define SELECT_ERROR 1 @@ -203,7 +208,7 @@ static CURLcode CONNECT(struct connectdata *conn, if(TUNNEL_INIT == s->tunnel_state) { /* BEGIN CONNECT PHASE */ char *host_port; - Curl_send_buffer *req_buffer; + struct dynbuf req; infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port); @@ -214,17 +219,12 @@ static CURLcode CONNECT(struct connectdata *conn, free(data->req.newurl); data->req.newurl = NULL; - /* initialize a dynamic send-buffer */ - req_buffer = Curl_add_buffer_init(); - - if(!req_buffer) - return CURLE_OUT_OF_MEMORY; - host_port = aprintf("%s:%d", hostname, remote_port); - if(!host_port) { - Curl_add_buffer_free(&req_buffer); + if(!host_port) return CURLE_OUT_OF_MEMORY; - } + + /* initialize a dynamic send-buffer */ + Curl_dyn_init(&req, DYN_HTTP_REQUEST); /* Setup the proxy-authorization header, if any */ result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE); @@ -235,8 +235,8 @@ static CURLcode CONNECT(struct connectdata *conn, char *host = NULL; const char *proxyconn = ""; const char *useragent = ""; - const char *http = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? - "1.0" : "1.1"; + const char *httpv = + (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1"; bool ipv6_ip = conn->bits.ipv6_ip; char *hostheader; @@ -247,7 +247,7 @@ static CURLcode CONNECT(struct connectdata *conn, aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", remote_port); if(!hostheader) { - Curl_add_buffer_free(&req_buffer); + Curl_dyn_free(&req); return CURLE_OUT_OF_MEMORY; } @@ -255,7 +255,7 @@ static CURLcode CONNECT(struct connectdata *conn, host = aprintf("Host: %s\r\n", hostheader); if(!host) { free(hostheader); - Curl_add_buffer_free(&req_buffer); + Curl_dyn_free(&req); return CURLE_OUT_OF_MEMORY; } } @@ -264,52 +264,49 @@ static CURLcode CONNECT(struct connectdata *conn, if(!Curl_checkProxyheaders(conn, "User-Agent") && data->set.str[STRING_USERAGENT]) - useragent = conn->allocptr.uagent; + useragent = data->state.aptr.uagent; result = - Curl_add_bufferf(&req_buffer, - "CONNECT %s HTTP/%s\r\n" - "%s" /* Host: */ - "%s" /* Proxy-Authorization */ - "%s" /* User-Agent */ - "%s", /* Proxy-Connection */ - hostheader, - http, - host?host:"", - conn->allocptr.proxyuserpwd? - conn->allocptr.proxyuserpwd:"", - useragent, - proxyconn); + Curl_dyn_addf(&req, + "CONNECT %s HTTP/%s\r\n" + "%s" /* Host: */ + "%s" /* Proxy-Authorization */ + "%s" /* User-Agent */ + "%s", /* Proxy-Connection */ + hostheader, + httpv, + host?host:"", + data->state.aptr.proxyuserpwd? + data->state.aptr.proxyuserpwd:"", + useragent, + proxyconn); if(host) free(host); free(hostheader); if(!result) - result = Curl_add_custom_headers(conn, TRUE, req_buffer); + result = Curl_add_custom_headers(conn, TRUE, &req); if(!result) /* CRLF terminate the request */ - result = Curl_add_bufferf(&req_buffer, "\r\n"); + result = Curl_dyn_add(&req, "\r\n"); if(!result) { /* Send the connect request to the proxy */ /* BLOCKING */ - result = - Curl_add_buffer_send(&req_buffer, conn, - &data->info.request_size, 0, sockindex); + result = Curl_buffer_send(&req, conn, &data->info.request_size, 0, + sockindex); } - req_buffer = NULL; if(result) failf(data, "Failed sending CONNECT to proxy"); } - Curl_add_buffer_free(&req_buffer); + Curl_dyn_free(&req); if(result) return result; s->tunnel_state = TUNNEL_CONNECT; - s->perline = 0; } /* END CONNECT PHASE */ check = Curl_timeleft(data, NULL, TRUE); @@ -327,18 +324,13 @@ static CURLcode CONNECT(struct connectdata *conn, { /* READING RESPONSE PHASE */ int error = SELECT_OK; - while(s->keepon && !error) { + while(s->keepon) { ssize_t gotbytes; - - /* make sure we have space to read more data */ - if(s->ptr >= &s->connect_buffer[CONNECT_BUFFER_SIZE]) { - failf(data, "CONNECT response too large!"); - return CURLE_RECV_ERROR; - } + char byte; /* Read one byte at a time to avoid a race condition. Wait at most one second before looping to ensure continuous pgrsUpdates. */ - result = Curl_read(conn, tunnelsocket, s->ptr, 1, &gotbytes); + result = Curl_read(conn, tunnelsocket, &byte, 1, &gotbytes); if(result == CURLE_AGAIN) /* socket buffer drained, return */ return CURLE_OK; @@ -365,11 +357,9 @@ static CURLcode CONNECT(struct connectdata *conn, break; } - if(s->keepon > TRUE) { /* This means we are currently ignoring a response-body */ - s->ptr = s->connect_buffer; if(s->cl) { /* A Content-Length based body: simply count down the counter and make sure to break out of the loop when we're done! */ @@ -384,11 +374,12 @@ static CURLcode CONNECT(struct connectdata *conn, /* chunked-encoded body, so we need to do the chunked dance properly to know when the end of the body is reached */ CHUNKcode r; + CURLcode extra; ssize_t tookcareof = 0; /* now parse the chunked piece of data so that we can properly tell when the stream ends */ - r = Curl_httpchunk_read(conn, s->ptr, 1, &tookcareof); + r = Curl_httpchunk_read(conn, &byte, 1, &tookcareof, &extra); if(r == CHUNKE_STOP) { /* we're done reading chunks! */ infof(data, "chunk reading DONE\n"); @@ -400,25 +391,27 @@ static CURLcode CONNECT(struct connectdata *conn, continue; } - s->perline++; /* amount of bytes in this line so far */ + if(Curl_dyn_addn(&s->rcvbuf, &byte, 1)) { + failf(data, "CONNECT response too large!"); + return CURLE_RECV_ERROR; + } /* if this is not the end of a header line then continue */ - if(*s->ptr != 0x0a) { - s->ptr++; + if(byte != 0x0a) continue; - } + + linep = Curl_dyn_ptr(&s->rcvbuf); + perline = Curl_dyn_len(&s->rcvbuf); /* amount of bytes in this line */ /* convert from the network encoding */ - result = Curl_convert_from_network(data, s->line_start, - (size_t)s->perline); + result = Curl_convert_from_network(data, linep, perline); /* Curl_convert_from_network calls failf if unsuccessful */ if(result) return result; /* output debug if that is requested */ if(data->set.verbose) - Curl_debug(data, CURLINFO_HEADER_IN, - s->line_start, (size_t)s->perline); + Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); if(!data->set.suppress_connect_headers) { /* send the header to the callback */ @@ -426,23 +419,22 @@ static CURLcode CONNECT(struct connectdata *conn, if(data->set.include_header) writetype |= CLIENTWRITE_BODY; - result = Curl_client_write(conn, writetype, - s->line_start, s->perline); + result = Curl_client_write(conn, writetype, linep, perline); if(result) return result; } - data->info.header_size += (long)s->perline; - data->req.headerbytecount += (long)s->perline; + data->info.header_size += (long)perline; + data->req.headerbytecount += (long)perline; /* Newlines are CRLF, so the CR is ignored as the line isn't really terminated until the LF comes. Treat a following CR as end-of-headers as well.*/ - if(('\r' == s->line_start[0]) || - ('\n' == s->line_start[0])) { + if(('\r' == linep[0]) || + ('\n' == linep[0])) { /* end of response-headers from the proxy */ - s->ptr = s->connect_buffer; + if((407 == k->httpcode) && !data->state.authproblem) { /* If we get a 407 response code with content length when we have no auth problem, we must ignore the @@ -455,24 +447,23 @@ static CURLcode CONNECT(struct connectdata *conn, } else if(s->chunked_encoding) { CHUNKcode r; + CURLcode extra; infof(data, "Ignore chunked response-body\n"); - /* We set ignorebody true here since the chunked - decoder function will acknowledge that. Pay - attention so that this is cleared again when this - function returns! */ + /* We set ignorebody true here since the chunked decoder + function will acknowledge that. Pay attention so that this is + cleared again when this function returns! */ k->ignorebody = TRUE; - if(s->line_start[1] == '\n') { - /* this can only be a LF if the letter at index 0 - was a CR */ - s->line_start++; - } + if(linep[1] == '\n') + /* this can only be a LF if the letter at index 0 was a CR */ + linep++; - /* now parse the chunked piece of data so that we can - properly tell when the stream ends */ - r = Curl_httpchunk_read(conn, s->line_start + 1, 1, &gotbytes); + /* now parse the chunked piece of data so that we can properly + tell when the stream ends */ + r = Curl_httpchunk_read(conn, linep + 1, 1, &gotbytes, + &extra); if(r == CHUNKE_STOP) { /* we're done reading chunks! */ infof(data, "chunk reading DONE\n"); @@ -496,14 +487,13 @@ static CURLcode CONNECT(struct connectdata *conn, continue; } - s->line_start[s->perline] = 0; /* zero terminate the buffer */ - if((checkprefix("WWW-Authenticate:", s->line_start) && + if((checkprefix("WWW-Authenticate:", linep) && (401 == k->httpcode)) || - (checkprefix("Proxy-authenticate:", s->line_start) && + (checkprefix("Proxy-authenticate:", linep) && (407 == k->httpcode))) { bool proxy = (k->httpcode == 407) ? TRUE : FALSE; - char *auth = Curl_copy_header_value(s->line_start); + char *auth = Curl_copy_header_value(linep); if(!auth) return CURLE_OUT_OF_MEMORY; @@ -514,7 +504,7 @@ static CURLcode CONNECT(struct connectdata *conn, if(result) return result; } - else if(checkprefix("Content-Length:", s->line_start)) { + else if(checkprefix("Content-Length:", linep)) { if(k->httpcode/100 == 2) { /* A client MUST ignore any Content-Length or Transfer-Encoding header fields received in a successful response to CONNECT. @@ -523,13 +513,13 @@ static CURLcode CONNECT(struct connectdata *conn, k->httpcode); } else { - (void)curlx_strtoofft(s->line_start + + (void)curlx_strtoofft(linep + strlen("Content-Length:"), NULL, 10, &s->cl); } } - else if(Curl_compareheader(s->line_start, "Connection:", "close")) + else if(Curl_compareheader(linep, "Connection:", "close")) s->close_connection = TRUE; - else if(checkprefix("Transfer-Encoding:", s->line_start)) { + else if(checkprefix("Transfer-Encoding:", linep)) { if(k->httpcode/100 == 2) { /* A client MUST ignore any Content-Length or Transfer-Encoding header fields received in a successful response to CONNECT. @@ -537,7 +527,7 @@ static CURLcode CONNECT(struct connectdata *conn, infof(data, "Ignoring Transfer-Encoding in " "CONNECT %03d response\n", k->httpcode); } - else if(Curl_compareheader(s->line_start, + else if(Curl_compareheader(linep, "Transfer-Encoding:", "chunked")) { infof(data, "CONNECT responded chunked\n"); s->chunked_encoding = TRUE; @@ -545,19 +535,16 @@ static CURLcode CONNECT(struct connectdata *conn, Curl_httpchunk_init(conn); } } - else if(Curl_compareheader(s->line_start, - "Proxy-Connection:", "close")) + else if(Curl_compareheader(linep, "Proxy-Connection:", "close")) s->close_connection = TRUE; - else if(2 == sscanf(s->line_start, "HTTP/1.%d %d", + else if(2 == sscanf(linep, "HTTP/1.%d %d", &subversion, &k->httpcode)) { /* store the HTTP code from the proxy */ data->info.httpproxycode = k->httpcode; } - s->perline = 0; /* line starts over here */ - s->ptr = s->connect_buffer; - s->line_start = s->ptr; + Curl_dyn_reset(&s->rcvbuf); } /* while there's buffer left and loop is requested */ if(Curl_pgrsUpdate(conn)) @@ -618,6 +605,7 @@ static CURLcode CONNECT(struct connectdata *conn, if(conn->bits.proxy_connect_closed) /* this is not an error, just part of the connection negotiation */ return CURLE_OK; + Curl_dyn_free(&s->rcvbuf); failf(data, "Received HTTP code %d from proxy after CONNECT", data->req.httpcode); return CURLE_RECV_ERROR; @@ -628,16 +616,18 @@ static CURLcode CONNECT(struct connectdata *conn, /* If a proxy-authorization header was used for the proxy, then we should make sure that it isn't accidentally used for the document request after we've connected. So let's free and clear it here. */ - Curl_safefree(conn->allocptr.proxyuserpwd); - conn->allocptr.proxyuserpwd = NULL; + Curl_safefree(data->state.aptr.proxyuserpwd); + data->state.aptr.proxyuserpwd = NULL; data->state.authproxy.done = TRUE; + data->state.authproxy.multipass = FALSE; infof(data, "Proxy replied %d to CONNECT request\n", data->info.httpproxycode); data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */ conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the document request */ + Curl_dyn_free(&s->rcvbuf); return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h index e19fa859a06..29988a69639 100644 --- a/Utilities/cmcurl/lib/http_proxy.h +++ b/Utilities/cmcurl/lib/http_proxy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -47,5 +47,6 @@ bool Curl_connect_ongoing(struct connectdata *conn); #endif void Curl_connect_free(struct Curl_easy *data); +void Curl_connect_done(struct Curl_easy *data); #endif /* HEADER_CURL_HTTP_PROXY_H */ diff --git a/Utilities/cmcurl/lib/idn_win32.c b/Utilities/cmcurl/lib/idn_win32.c index 8dc300b36d0..2f5850dd06e 100644 --- a/Utilities/cmcurl/lib/idn_win32.c +++ b/Utilities/cmcurl/lib/idn_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -72,13 +72,13 @@ bool curl_win32_idn_to_ascii(const char *in, char **out) { bool success = FALSE; - wchar_t *in_w = Curl_convert_UTF8_to_wchar(in); + wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); if(in_w) { wchar_t punycode[IDN_MAX_LENGTH]; int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH); free(in_w); if(chars) { - *out = Curl_convert_wchar_to_UTF8(punycode); + *out = curlx_convert_wchar_to_UTF8(punycode); if(*out) success = TRUE; } @@ -91,7 +91,7 @@ bool curl_win32_ascii_to_idn(const char *in, char **out) { bool success = FALSE; - wchar_t *in_w = Curl_convert_UTF8_to_wchar(in); + wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); if(in_w) { size_t in_len = wcslen(in_w) + 1; wchar_t unicode[IDN_MAX_LENGTH]; @@ -99,7 +99,7 @@ bool curl_win32_ascii_to_idn(const char *in, char **out) unicode, IDN_MAX_LENGTH); free(in_w); if(chars) { - *out = Curl_convert_wchar_to_UTF8(unicode); + *out = curlx_convert_wchar_to_UTF8(unicode); if(*out) success = TRUE; } diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c index d003de6783e..39388692ef0 100644 --- a/Utilities/cmcurl/lib/if2ip.c +++ b/Utilities/cmcurl/lib/if2ip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -118,7 +118,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, if(iface->ifa_addr->sa_family == af) { if(strcasecompare(iface->ifa_name, interf)) { void *addr; - char *ip; + const char *ip; char scope[12] = ""; char ipstr[64]; #ifdef ENABLE_IPV6 @@ -129,11 +129,11 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr); if(ifscope != remote_scope) { - /* We are interested only in interface addresses whose - scope matches the remote address we want to - connect to: global for global, link-local for - link-local, etc... */ - if(res == IF2IP_NOT_FOUND) res = IF2IP_AF_NOT_SUPPORTED; + /* We are interested only in interface addresses whose scope + matches the remote address we want to connect to: global + for global, link-local for link-local, etc... */ + if(res == IF2IP_NOT_FOUND) + res = IF2IP_AF_NOT_SUPPORTED; continue; } @@ -153,15 +153,15 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, } if(scopeid) - msnprintf(scope, sizeof(scope), "%%%u", scopeid); + msnprintf(scope, sizeof(scope), "%%%u", scopeid); #endif } else #endif addr = - &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr; + &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr; res = IF2IP_FOUND; - ip = (char *) Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr)); + ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr)); msnprintf(buf, buf_size, "%s%s", ip, scope); break; } @@ -190,6 +190,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, struct sockaddr_in *s; curl_socket_t dummy; size_t len; + const char *r; (void)remote_scope; (void)local_scope_id; @@ -219,9 +220,11 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, s = (struct sockaddr_in *)(void *)&req.ifr_addr; memcpy(&in, &s->sin_addr, sizeof(in)); - Curl_inet_ntop(s->sin_family, &in, buf, buf_size); + r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size); sclose(dummy); + if(!r) + return IF2IP_NOT_FOUND; return IF2IP_FOUND; } diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index bdcc69c67a7..cad0e5908d9 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -95,8 +95,7 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status, static CURLcode imap_connect(struct connectdata *conn, bool *done); static CURLcode imap_disconnect(struct connectdata *conn, bool dead); static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done); -static int imap_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); +static int imap_getsock(struct connectdata *conn, curl_socket_t *socks); static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done); static CURLcode imap_setup_connection(struct connectdata *conn); static char *imap_atom(const char *str, bool escape_only); @@ -188,7 +187,7 @@ static void imap_to_imaps(struct connectdata *conn) conn->handler = &Curl_handler_imaps; /* Set the connection's upgraded to TLS flag */ - conn->tls_upgraded = TRUE; + conn->bits.tls_upgraded = TRUE; } #else #define imap_to_imaps(x) Curl_nop_stmt @@ -444,10 +443,8 @@ static CURLcode imap_perform_capability(struct connectdata *conn) */ static CURLcode imap_perform_starttls(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* Send the STARTTLS command */ - result = imap_sendf(conn, "STARTTLS"); + CURLcode result = imap_sendf(conn, "STARTTLS"); if(!result) state(conn, IMAP_STARTTLS); @@ -463,11 +460,10 @@ static CURLcode imap_perform_starttls(struct connectdata *conn) */ static CURLcode imap_perform_upgrade_tls(struct connectdata *conn) { - CURLcode result = CURLE_OK; - struct imap_conn *imapc = &conn->proto.imapc; - /* Start the SSL connection */ - result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone); + struct imap_conn *imapc = &conn->proto.imapc; + CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, + &imapc->ssldone); if(!result) { if(imapc->state != IMAP_UPGRADETLS) @@ -826,10 +822,8 @@ static CURLcode imap_perform_search(struct connectdata *conn) */ static CURLcode imap_perform_logout(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* Send the LOGOUT command */ - result = imap_sendf(conn, "LOGOUT"); + CURLcode result = imap_sendf(conn, "LOGOUT"); if(!result) state(conn, IMAP_LOGOUT); @@ -1312,6 +1306,7 @@ static CURLcode imap_statemach_act(struct connectdata *conn) break; case IMAP_LIST: + case IMAP_SEARCH: result = imap_state_listsearch_resp(conn, imapcode, imapc->state); break; @@ -1335,10 +1330,6 @@ static CURLcode imap_statemach_act(struct connectdata *conn) result = imap_state_append_final_resp(conn, imapcode, imapc->state); break; - case IMAP_SEARCH: - result = imap_state_listsearch_resp(conn, imapcode, imapc->state); - break; - case IMAP_LOGOUT: /* fallthrough, just stop! */ default: @@ -1397,10 +1388,9 @@ static CURLcode imap_init(struct connectdata *conn) } /* For the IMAP "protocol connect" and "doing" phases only */ -static int imap_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +static int imap_getsock(struct connectdata *conn, curl_socket_t *socks) { - return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks); + return Curl_pp_getsock(&conn->proto.imapc.pp, socks); } /*********************************************************************** @@ -1720,7 +1710,7 @@ static CURLcode imap_setup_connection(struct connectdata *conn) return result; /* Clear the TLS upgraded flag */ - conn->tls_upgraded = FALSE; + conn->bits.tls_upgraded = FALSE; return CURLE_OK; } @@ -1967,7 +1957,7 @@ static CURLcode imap_parse_url_path(struct connectdata *conn) end--; result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL, - TRUE); + REJECT_CTRL); if(result) return result; } @@ -1989,7 +1979,8 @@ static CURLcode imap_parse_url_path(struct connectdata *conn) return CURLE_URL_MALFORMAT; /* Decode the name parameter */ - result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE); + result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, + REJECT_CTRL); if(result) return result; @@ -1999,7 +1990,8 @@ static CURLcode imap_parse_url_path(struct connectdata *conn) ptr++; /* Decode the value parameter */ - result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE); + result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, + REJECT_CTRL); if(result) { free(name); return result; @@ -2087,7 +2079,7 @@ static CURLcode imap_parse_custom_request(struct connectdata *conn) if(custom) { /* URL decode the custom request */ - result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE); + result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, REJECT_CTRL); /* Extract the parameters if specified */ if(!result) { diff --git a/Utilities/cmcurl/lib/imap.h b/Utilities/cmcurl/lib/imap.h index 0efcfd293cc..4786f562418 100644 --- a/Utilities/cmcurl/lib/imap.h +++ b/Utilities/cmcurl/lib/imap.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2009 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 2009 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/inet_ntop.c b/Utilities/cmcurl/lib/inet_ntop.c index 855981c666d..9a5af7f4218 100644 --- a/Utilities/cmcurl/lib/inet_ntop.c +++ b/Utilities/cmcurl/lib/inet_ntop.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1996-2001 Internet Software Consortium. + * Copyright (C) 1996-2019 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/Utilities/cmcurl/lib/inet_ntop.h b/Utilities/cmcurl/lib/inet_ntop.h index d150bb6937b..9d3f237f37e 100644 --- a/Utilities/cmcurl/lib/inet_ntop.h +++ b/Utilities/cmcurl/lib/inet_ntop.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c index 0d65ae0ec70..9c87a05620f 100644 --- a/Utilities/cmcurl/lib/inet_pton.c +++ b/Utilities/cmcurl/lib/inet_pton.c @@ -1,6 +1,6 @@ /* This is from the BIND 4.9.4 release, modified to compile by itself */ -/* Copyright (c) 1996 by Internet Software Consortium. +/* Copyright (c) 1996 - 2019 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/Utilities/cmcurl/lib/inet_pton.h b/Utilities/cmcurl/lib/inet_pton.h index 0209b9b7b72..e695af9c666 100644 --- a/Utilities/cmcurl/lib/inet_pton.h +++ b/Utilities/cmcurl/lib/inet_pton.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c index 147ab024e21..5bd8e7181a9 100644 --- a/Utilities/cmcurl/lib/krb5.c +++ b/Utilities/cmcurl/lib/krb5.c @@ -1,8 +1,8 @@ /* GSSAPI/krb5 support for FTP - loosely based on old krb4.c * - * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan + * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). - * Copyright (c) 2004 - 2017 Daniel Stenberg + * Copyright (c) 2004 - 2019 Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -320,10 +320,8 @@ static void krb5_end(void *app_data) OM_uint32 min; gss_ctx_id_t *context = app_data; if(*context != GSS_C_NO_CONTEXT) { -#ifdef DEBUGBUILD - OM_uint32 maj = -#endif - gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER); + OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER); + (void)maj; DEBUGASSERT(maj == GSS_S_COMPLETE); } } diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index fd31faa3e73..512def659c0 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -75,7 +75,7 @@ /* Use our own implementation. */ -typedef struct { +struct ldap_urldesc { char *lud_host; int lud_port; #if defined(USE_WIN32_LDAP) @@ -95,10 +95,10 @@ typedef struct { size_t lud_attrs_dups; /* how many were dup'ed, this field is not in the "real" struct so can only be used in code without HAVE_LDAP_URL_PARSE defined */ -} CURL_LDAPURLDesc; +}; #undef LDAPURLDesc -#define LDAPURLDesc CURL_LDAPURLDesc +#define LDAPURLDesc struct ldap_urldesc static int _ldap_url_parse(const struct connectdata *conn, LDAPURLDesc **ludp); @@ -112,13 +112,19 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp); #define LDAP_TRACE(x) do { \ _ldap_trace("%u: ", __LINE__); \ _ldap_trace x; \ - } WHILE_FALSE + } while(0) static void _ldap_trace(const char *fmt, ...); #else #define LDAP_TRACE(x) Curl_nop_stmt #endif +#if defined(USE_WIN32_LDAP) && defined(ldap_err2string) +/* Use ansi error strings in UNICODE builds */ +#undef ldap_err2string +#define ldap_err2string ldap_err2stringA +#endif + static CURLcode Curl_ldap(struct connectdata *conn, bool *done); @@ -233,13 +239,13 @@ static int ldap_win_bind(struct connectdata *conn, LDAP *server, PTCHAR inpass = NULL; if(user && passwd && (conn->data->set.httpauth & CURLAUTH_BASIC)) { - inuser = Curl_convert_UTF8_to_tchar((char *) user); - inpass = Curl_convert_UTF8_to_tchar((char *) passwd); + inuser = curlx_convert_UTF8_to_tchar((char *) user); + inpass = curlx_convert_UTF8_to_tchar((char *) passwd); rc = ldap_simple_bind_s(server, inuser, inpass); - Curl_unicodefree(inuser); - Curl_unicodefree(inpass); + curlx_unicodefree(inuser); + curlx_unicodefree(inpass); } #if defined(USE_WINDOWS_SSPI) else { @@ -300,7 +306,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) ldap_ssl ? "encrypted" : "cleartext"); #if defined(USE_WIN32_LDAP) - host = Curl_convert_UTF8_to_tchar(conn->host.name); + host = curlx_convert_UTF8_to_tchar(conn->host.name); if(!host) { result = CURLE_OUT_OF_MEMORY; @@ -511,7 +517,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) size_t name_len; #if defined(USE_WIN32_LDAP) TCHAR *dn = ldap_get_dn(server, entryIterator); - name = Curl_convert_tchar_to_UTF8(dn); + name = curlx_convert_tchar_to_UTF8(dn); if(!name) { ldap_memfree(dn); @@ -527,7 +533,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4); if(result) { #if defined(USE_WIN32_LDAP) - Curl_unicodefree(name); + curlx_unicodefree(name); #endif ldap_memfree(dn); @@ -538,7 +544,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) name_len); if(result) { #if defined(USE_WIN32_LDAP) - Curl_unicodefree(name); + curlx_unicodefree(name); #endif ldap_memfree(dn); @@ -548,7 +554,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); if(result) { #if defined(USE_WIN32_LDAP) - Curl_unicodefree(name); + curlx_unicodefree(name); #endif ldap_memfree(dn); @@ -558,7 +564,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) dlsize += name_len + 5; #if defined(USE_WIN32_LDAP) - Curl_unicodefree(name); + curlx_unicodefree(name); #endif ldap_memfree(dn); } @@ -570,7 +576,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) BerValue **vals; size_t attr_len; #if defined(USE_WIN32_LDAP) - char *attr = Curl_convert_tchar_to_UTF8(attribute); + char *attr = curlx_convert_tchar_to_UTF8(attribute); if(!attr) { if(ber) ber_free(ber, 0); @@ -591,7 +597,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(result) { ldap_value_free_len(vals); #if defined(USE_WIN32_LDAP) - Curl_unicodefree(attr); + curlx_unicodefree(attr); #endif ldap_memfree(attribute); if(ber) @@ -605,7 +611,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(result) { ldap_value_free_len(vals); #if defined(USE_WIN32_LDAP) - Curl_unicodefree(attr); + curlx_unicodefree(attr); #endif ldap_memfree(attribute); if(ber) @@ -618,7 +624,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(result) { ldap_value_free_len(vals); #if defined(USE_WIN32_LDAP) - Curl_unicodefree(attr); + curlx_unicodefree(attr); #endif ldap_memfree(attribute); if(ber) @@ -640,7 +646,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(result) { ldap_value_free_len(vals); #if defined(USE_WIN32_LDAP) - Curl_unicodefree(attr); + curlx_unicodefree(attr); #endif ldap_memfree(attribute); if(ber) @@ -656,7 +662,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(result) { ldap_value_free_len(vals); #if defined(USE_WIN32_LDAP) - Curl_unicodefree(attr); + curlx_unicodefree(attr); #endif ldap_memfree(attribute); if(ber) @@ -674,7 +680,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(result) { ldap_value_free_len(vals); #if defined(USE_WIN32_LDAP) - Curl_unicodefree(attr); + curlx_unicodefree(attr); #endif ldap_memfree(attribute); if(ber) @@ -690,7 +696,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(result) { ldap_value_free_len(vals); #if defined(USE_WIN32_LDAP) - Curl_unicodefree(attr); + curlx_unicodefree(attr); #endif ldap_memfree(attribute); if(ber) @@ -708,7 +714,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) /* Free the attribute as we are done with it */ #if defined(USE_WIN32_LDAP) - Curl_unicodefree(attr); + curlx_unicodefree(attr); #endif ldap_memfree(attribute); @@ -740,7 +746,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) #endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */ #if defined(USE_WIN32_LDAP) - Curl_unicodefree(host); + curlx_unicodefree(host); #endif /* no data to transfer */ @@ -838,10 +844,10 @@ static bool split_str(char *str, char ***out, size_t *count) static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) { int rc = LDAP_SUCCESS; - char *path; - char *query; char *p; - char *q; + char *path; + char *q = NULL; + char *query = NULL; size_t i; if(!conn->data || @@ -859,11 +865,13 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) if(!path) return LDAP_NO_MEMORY; - /* Duplicate the query */ - q = query = strdup(conn->data->state.up.query); - if(!query) { - free(path); - return LDAP_NO_MEMORY; + /* Duplicate the query if present */ + if(conn->data->state.up.query) { + q = query = strdup(conn->data->state.up.query); + if(!query) { + free(path); + return LDAP_NO_MEMORY; + } } /* Parse the DN (Distinguished Name) */ @@ -875,7 +883,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) LDAP_TRACE(("DN '%s'\n", dn)); /* Unescape the DN */ - result = Curl_urldecode(conn->data, dn, 0, &unescaped, NULL, FALSE); + result = Curl_urldecode(conn->data, dn, 0, &unescaped, NULL, REJECT_ZERO); if(result) { rc = LDAP_NO_MEMORY; @@ -884,10 +892,10 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) #if defined(USE_WIN32_LDAP) /* Convert the unescaped string to a tchar */ - ludp->lud_dn = Curl_convert_UTF8_to_tchar(unescaped); + ludp->lud_dn = curlx_convert_UTF8_to_tchar(unescaped); /* Free the unescaped string as we are done with it */ - Curl_unicodefree(unescaped); + curlx_unicodefree(unescaped); if(!ludp->lud_dn) { rc = LDAP_NO_MEMORY; @@ -941,7 +949,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) /* Unescape the attribute */ result = Curl_urldecode(conn->data, attributes[i], 0, &unescaped, NULL, - FALSE); + REJECT_ZERO); if(result) { free(attributes); @@ -952,10 +960,10 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) #if defined(USE_WIN32_LDAP) /* Convert the unescaped string to a tchar */ - ludp->lud_attrs[i] = Curl_convert_UTF8_to_tchar(unescaped); + ludp->lud_attrs[i] = curlx_convert_UTF8_to_tchar(unescaped); /* Free the unescaped string as we are done with it */ - Curl_unicodefree(unescaped); + curlx_unicodefree(unescaped); if(!ludp->lud_attrs[i]) { free(attributes); @@ -1010,7 +1018,8 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) LDAP_TRACE(("filter '%s'\n", filter)); /* Unescape the filter */ - result = Curl_urldecode(conn->data, filter, 0, &unescaped, NULL, FALSE); + result = Curl_urldecode(conn->data, filter, 0, &unescaped, NULL, + REJECT_ZERO); if(result) { rc = LDAP_NO_MEMORY; @@ -1019,10 +1028,10 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) #if defined(USE_WIN32_LDAP) /* Convert the unescaped string to a tchar */ - ludp->lud_filter = Curl_convert_UTF8_to_tchar(unescaped); + ludp->lud_filter = curlx_convert_UTF8_to_tchar(unescaped); /* Free the unescaped string as we are done with it */ - Curl_unicodefree(unescaped); + curlx_unicodefree(unescaped); if(!ludp->lud_filter) { rc = LDAP_NO_MEMORY; diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c index f8769c2af84..e7c6f51dc73 100644 --- a/Utilities/cmcurl/lib/llist.c +++ b/Utilities/cmcurl/lib/llist.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -144,54 +144,3 @@ Curl_llist_count(struct curl_llist *list) { return list->size; } - -/* - * @unittest: 1300 - */ -void Curl_llist_move(struct curl_llist *list, struct curl_llist_element *e, - struct curl_llist *to_list, - struct curl_llist_element *to_e) -{ - /* Remove element from list */ - if(e == NULL || list->size == 0) - return; - - if(e == list->head) { - list->head = e->next; - - if(list->head == NULL) - list->tail = NULL; - else - e->next->prev = NULL; - } - else { - e->prev->next = e->next; - if(!e->next) - list->tail = e->prev; - else - e->next->prev = e->prev; - } - - --list->size; - - /* Add element to to_list after to_e */ - if(to_list->size == 0) { - to_list->head = e; - to_list->head->prev = NULL; - to_list->head->next = NULL; - to_list->tail = e; - } - else { - e->next = to_e->next; - e->prev = to_e; - if(to_e->next) { - to_e->next->prev = e; - } - else { - to_list->tail = e; - } - to_e->next = e; - } - - ++to_list->size; -} diff --git a/Utilities/cmcurl/lib/llist.h b/Utilities/cmcurl/lib/llist.h index b9d4c89a981..0178c425980 100644 --- a/Utilities/cmcurl/lib/llist.h +++ b/Utilities/cmcurl/lib/llist.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -47,7 +47,4 @@ void Curl_llist_remove(struct curl_llist *, struct curl_llist_element *, void *); size_t Curl_llist_count(struct curl_llist *); void Curl_llist_destroy(struct curl_llist *, void *); -void Curl_llist_move(struct curl_llist *, struct curl_llist_element *, - struct curl_llist *, struct curl_llist_element *); - #endif /* HEADER_CURL_LLIST_H */ diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c index e7c77bc36ed..0fab52def72 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c @@ -1,5 +1,231 @@ -/* - * !checksrc! disable COPYRIGHT +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_CRYPTO_AUTH) + +#include "curl_md4.h" +#include "warnless.h" + +#ifdef USE_OPENSSL +#include +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) +/* OpenSSL 3.0.0 marks the MD4 functions as deprecated */ +#define OPENSSL_NO_MD4 +#endif +#endif /* USE_OPENSSL */ + +#ifdef USE_MBEDTLS +#include +#include + +#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) + #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS +#endif +#endif /* USE_MBEDTLS */ + +#if defined(USE_GNUTLS_NETTLE) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +typedef struct md4_ctx MD4_CTX; + +static void MD4_Init(MD4_CTX *ctx) +{ + md4_init(ctx); +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + md4_update(ctx, size, data); +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + md4_digest(ctx, MD4_DIGEST_SIZE, result); +} + +#elif defined(USE_GNUTLS) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +typedef gcry_md_hd_t MD4_CTX; + +static void MD4_Init(MD4_CTX *ctx) +{ + gcry_md_open(ctx, GCRY_MD_MD4, 0); +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + gcry_md_write(*ctx, data, size); +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + memcpy(result, gcry_md_read(*ctx, 0), MD4_DIGEST_LENGTH); + gcry_md_close(*ctx); +} + +#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) +/* When OpenSSL is available we use the MD4-functions from OpenSSL */ +#include + +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +typedef CC_MD4_CTX MD4_CTX; + +static void MD4_Init(MD4_CTX *ctx) +{ + (void)CC_MD4_Init(ctx); +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + (void)CC_MD4_Update(ctx, data, (CC_LONG)size); +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + (void)CC_MD4_Final(result, ctx); +} + +#elif defined(USE_WIN32_CRYPTO) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +struct md4_ctx { + HCRYPTPROV hCryptProv; + HCRYPTHASH hHash; +}; +typedef struct md4_ctx MD4_CTX; + +static void MD4_Init(MD4_CTX *ctx) +{ + ctx->hCryptProv = 0; + ctx->hHash = 0; + + if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash); + } +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + CryptHashData(ctx->hHash, (BYTE *)data, (unsigned int) size, 0); +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + unsigned long length = 0; + + CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); + if(length == MD4_DIGEST_LENGTH) + CryptGetHashParam(ctx->hHash, HP_HASHVAL, result, &length, 0); + + if(ctx->hHash) + CryptDestroyHash(ctx->hHash); + + if(ctx->hCryptProv) + CryptReleaseContext(ctx->hCryptProv, 0); +} + +#elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C)) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +struct md4_ctx { + void *data; + unsigned long size; +}; +typedef struct md4_ctx MD4_CTX; + +static void MD4_Init(MD4_CTX *ctx) +{ + ctx->data = NULL; + ctx->size = 0; +} + +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +{ + if(ctx->data == NULL) { + ctx->data = malloc(size); + if(ctx->data != NULL) { + memcpy(ctx->data, data, size); + ctx->size = size; + } + } +} + +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +{ + if(ctx->data != NULL) { +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + mbedtls_md4(ctx->data, ctx->size, result); +#else + (void) mbedtls_md4_ret(ctx->data, ctx->size, result); +#endif + + Curl_safefree(ctx->data); + ctx->size = 0; + } +} + +#else +/* When no other crypto library is available, or the crypto library doesn't + * support MD4, we use this code segment this implementation of it + * * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. * MD4 Message-Digest Algorithm (RFC 1320). * @@ -36,31 +262,19 @@ * compile-time configuration. */ -#include "curl_setup.h" - -/* The NSS, OS/400, and when not included, OpenSSL and mbed TLS crypto - * libraries do not provide the MD4 hash algorithm, so we use this - * implementation of it */ -#if defined(USE_NSS) || defined(USE_OS400CRYPTO) || \ - (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) || \ - (defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C)) - -#include "curl_md4.h" -#include "warnless.h" - -#ifndef HAVE_OPENSSL #include /* Any 32-bit or wider unsigned integer data type will do */ typedef unsigned int MD4_u32plus; -typedef struct { +struct md4_ctx { MD4_u32plus lo, hi; MD4_u32plus a, b, c, d; unsigned char buffer[64]; MD4_u32plus block[16]; -} MD4_CTX; +}; +typedef struct md4_ctx MD4_CTX; static void MD4_Init(MD4_CTX *ctx); static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size); @@ -298,16 +512,16 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) memset(ctx, 0, sizeof(*ctx)); } -#endif +#endif /* CRYPTO LIBS */ -void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len) +void Curl_md4it(unsigned char *output, const unsigned char *input, + const size_t len) { MD4_CTX ctx; + MD4_Init(&ctx); MD4_Update(&ctx, input, curlx_uztoui(len)); MD4_Final(output, &ctx); } -#endif /* defined(USE_NSS) || defined(USE_OS400CRYPTO) || - (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) || - (defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C)) */ +#endif /* CURL_DISABLE_CRYPTO_AUTH */ diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c index 2b81ca455a9..557a51e6c0c 100644 --- a/Utilities/cmcurl/lib/md5.c +++ b/Utilities/cmcurl/lib/md5.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,6 +30,14 @@ #include "curl_hmac.h" #include "warnless.h" +#ifdef USE_MBEDTLS +#include + +#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) + #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS +#endif +#endif /* USE_MBEDTLS */ + #if defined(USE_GNUTLS_NETTLE) #include @@ -51,7 +59,7 @@ static void MD5_Update(MD5_CTX *ctx, md5_update(ctx, inputLen, input); } -static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) +static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) { md5_digest(ctx, 16, digest); } @@ -77,7 +85,7 @@ static void MD5_Update(MD5_CTX *ctx, gcry_md_write(*ctx, input, inputLen); } -static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) +static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) { memcpy(digest, gcry_md_read(*ctx, 0), 16); gcry_md_close(*ctx); @@ -90,6 +98,46 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) /* The last #include file should be: */ #include "memdebug.h" +#elif defined(USE_MBEDTLS) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +typedef mbedtls_md5_context MD5_CTX; + +static void MD5_Init(MD5_CTX *ctx) +{ +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + mbedtls_md5_starts(ctx); +#else + (void) mbedtls_md5_starts_ret(ctx); +#endif +} + +static void MD5_Update(MD5_CTX *ctx, + const unsigned char *data, + unsigned int length) +{ +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + mbedtls_md5_update(ctx, data, length); +#else + (void) mbedtls_md5_update_ret(ctx, data, length); +#endif +} + +static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) +{ +#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + mbedtls_md5_finish(ctx, digest); +#else + (void) mbedtls_md5_finish_ret(ctx, digest); +#endif +} + #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ @@ -119,27 +167,28 @@ static void MD5_Update(MD5_CTX *ctx, CC_MD5_Update(ctx, input, inputLen); } -static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) +static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) { CC_MD5_Final(digest, ctx); } -#elif defined(WIN32) && !defined(CURL_WINDOWS_APP) +#elif defined(USE_WIN32_CRYPTO) #include #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" -typedef struct { +struct md5_ctx { HCRYPTPROV hCryptProv; HCRYPTHASH hHash; -} MD5_CTX; +}; +typedef struct md5_ctx MD5_CTX; static void MD5_Init(MD5_CTX *ctx) { - if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, - PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash); } } @@ -151,7 +200,7 @@ static void MD5_Update(MD5_CTX *ctx, CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0); } -static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) +static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) { unsigned long length = 0; CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); @@ -164,7 +213,9 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) } #else + /* When no other crypto library is available we use this code segment */ + /* * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. * MD5 Message-Digest Algorithm (RFC 1321). @@ -211,12 +262,13 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) /* Any 32-bit or wider unsigned integer data type will do */ typedef unsigned int MD5_u32plus; -typedef struct { +struct md5_ctx { MD5_u32plus lo, hi; MD5_u32plus a, b, c, d; unsigned char buffer[64]; MD5_u32plus block[16]; -} MD5_CTX; +}; +typedef struct md5_ctx MD5_CTX; static void MD5_Init(MD5_CTX *ctx); static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); @@ -478,7 +530,7 @@ static void MD5_Final(unsigned char *result, MD5_CTX *ctx) #endif /* CRYPTO LIBS */ -const HMAC_params Curl_HMAC_MD5[] = { +const struct HMAC_params Curl_HMAC_MD5[] = { { /* Hash initialization function. */ CURLX_FUNCTION_CAST(HMAC_hinit_func, MD5_Init), @@ -495,7 +547,7 @@ const HMAC_params Curl_HMAC_MD5[] = { } }; -const MD5_params Curl_DIGEST_MD5[] = { +const struct MD5_params Curl_DIGEST_MD5[] = { { /* Digest initialization function */ CURLX_FUNCTION_CAST(Curl_MD5_init_func, MD5_Init), @@ -513,18 +565,19 @@ const MD5_params Curl_DIGEST_MD5[] = { /* * @unittest: 1601 */ -void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */ - const unsigned char *input) +void Curl_md5it(unsigned char *outbuffer, const unsigned char *input, + const size_t len) { MD5_CTX ctx; + MD5_Init(&ctx); - MD5_Update(&ctx, input, curlx_uztoui(strlen((char *)input))); + MD5_Update(&ctx, input, curlx_uztoui(len)); MD5_Final(outbuffer, &ctx); } -MD5_context *Curl_MD5_init(const MD5_params *md5params) +struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params) { - MD5_context *ctxt; + struct MD5_context *ctxt; /* Create MD5 context */ ctxt = malloc(sizeof(*ctxt)); @@ -546,7 +599,7 @@ MD5_context *Curl_MD5_init(const MD5_params *md5params) return ctxt; } -CURLcode Curl_MD5_update(MD5_context *context, +CURLcode Curl_MD5_update(struct MD5_context *context, const unsigned char *data, unsigned int len) { @@ -555,7 +608,7 @@ CURLcode Curl_MD5_update(MD5_context *context, return CURLE_OK; } -CURLcode Curl_MD5_final(MD5_context *context, unsigned char *result) +CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result) { (*context->md5_hash->md5_final_func)(result, context->md5_hashctx); diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c index ede60094bb4..1c6b151493a 100644 --- a/Utilities/cmcurl/lib/memdebug.c +++ b/Utilities/cmcurl/lib/memdebug.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -328,7 +328,7 @@ void curl_dbg_free(void *ptr, int line, const char *source) (Curl_cfree)(mem); } - if(source) + if(source && ptr) curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr); } diff --git a/Utilities/cmcurl/lib/memdebug.h b/Utilities/cmcurl/lib/memdebug.h index 5236f60fa54..7ca44262690 100644 --- a/Utilities/cmcurl/lib/memdebug.h +++ b/Utilities/cmcurl/lib/memdebug.h @@ -169,6 +169,6 @@ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source); */ #define Curl_safefree(ptr) \ - do { free((ptr)); (ptr) = NULL;} WHILE_FALSE + do { free((ptr)); (ptr) = NULL;} while(0) #endif /* HEADER_CURL_MEMDEBUG_H */ diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index 2135f72c25d..6a9b64a2da3 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,7 @@ #include "mime.h" #include "non-ascii.h" +#include "warnless.h" #include "urldata.h" #include "sendf.h" @@ -52,6 +53,10 @@ #define READ_ERROR ((size_t) -1) +#define STOP_FILLING ((size_t) -2) + +static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, + void *instream, bool *hasread); /* Encoders. */ static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, @@ -66,7 +71,7 @@ static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, curl_mimepart *part); static curl_off_t encoder_qp_size(curl_mimepart *part); -static const mime_encoder encoders[] = { +static const struct mime_encoder encoders[] = { {"binary", encoder_nop_read, encoder_nop_size}, {"8bit", encoder_nop_read, encoder_nop_size}, {"7bit", encoder_7bit_read, encoder_nop_size}, @@ -264,7 +269,8 @@ static char *Curl_basename(char *path) /* Set readback state. */ -static void mimesetstate(mime_state *state, enum mimestate tok, void *ptr) +static void mimesetstate(struct mime_state *state, + enum mimestate tok, void *ptr) { state->state = tok; state->ptr = ptr; @@ -337,7 +343,7 @@ static char *strippath(const char *fullfile) } /* Initialize data encoder state. */ -static void cleanup_encoder_state(mime_encoder_state *p) +static void cleanup_encoder_state(struct mime_encoder_state *p) { p->pos = 0; p->bufbeg = 0; @@ -347,17 +353,22 @@ static void cleanup_encoder_state(mime_encoder_state *p) /* Dummy encoder. This is used for 8bit and binary content encodings. */ static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part) + struct curl_mimepart *part) { - mime_encoder_state *st = &part->encstate; + struct mime_encoder_state *st = &part->encstate; size_t insize = st->bufend - st->bufbeg; (void) ateof; + if(!size) + return STOP_FILLING; + if(size > insize) size = insize; + if(size) - memcpy(buffer, st->buf, size); + memcpy(buffer, st->buf + st->bufbeg, size); + st->bufbeg += size; return size; } @@ -372,11 +383,14 @@ static curl_off_t encoder_nop_size(curl_mimepart *part) static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, curl_mimepart *part) { - mime_encoder_state *st = &part->encstate; + struct mime_encoder_state *st = &part->encstate; size_t cursize = st->bufend - st->bufbeg; (void) ateof; + if(!size) + return STOP_FILLING; + if(size > cursize) size = cursize; @@ -395,7 +409,7 @@ static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, curl_mimepart *part) { - mime_encoder_state *st = &part->encstate; + struct mime_encoder_state *st = &part->encstate; size_t cursize = 0; int i; char *ptr = buffer; @@ -404,8 +418,11 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, /* Line full ? */ if(st->pos > MAX_ENCODED_LINE_LENGTH - 4) { /* Yes, we need 2 characters for CRLF. */ - if(size < 2) + if(size < 2) { + if(!cursize) + return STOP_FILLING; break; + } *ptr++ = '\r'; *ptr++ = '\n'; st->pos = 0; @@ -414,7 +431,12 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, } /* Be sure there is enough space and input data for a base64 group. */ - if(size < 4 || st->bufend - st->bufbeg < 3) + if(size < 4) { + if(!cursize) + return STOP_FILLING; + break; + } + if(st->bufend - st->bufbeg < 3) break; /* Encode three bytes as four characters. */ @@ -431,25 +453,31 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, } /* If at eof, we have to flush the buffered data. */ - if(ateof && size >= 4) { - /* Buffered data size can only be 0, 1 or 2. */ - ptr[2] = ptr[3] = '='; - i = 0; - switch(st->bufend - st->bufbeg) { - case 2: - i = (st->buf[st->bufbeg + 1] & 0xFF) << 8; - /* FALLTHROUGH */ - case 1: - i |= (st->buf[st->bufbeg] & 0xFF) << 16; - ptr[0] = base64[(i >> 18) & 0x3F]; - ptr[1] = base64[(i >> 12) & 0x3F]; - if(++st->bufbeg != st->bufend) { - ptr[2] = base64[(i >> 6) & 0x3F]; - st->bufbeg++; + if(ateof) { + if(size < 4) { + if(!cursize) + return STOP_FILLING; + } + else { + /* Buffered data size can only be 0, 1 or 2. */ + ptr[2] = ptr[3] = '='; + i = 0; + switch(st->bufend - st->bufbeg) { + case 2: + i = (st->buf[st->bufbeg + 1] & 0xFF) << 8; + /* FALLTHROUGH */ + case 1: + i |= (st->buf[st->bufbeg] & 0xFF) << 16; + ptr[0] = base64[(i >> 18) & 0x3F]; + ptr[1] = base64[(i >> 12) & 0x3F]; + if(++st->bufbeg != st->bufend) { + ptr[2] = base64[(i >> 6) & 0x3F]; + st->bufbeg++; + } + cursize += 4; + st->pos += 4; + break; } - cursize += 4; - st->pos += 4; - break; } } @@ -485,7 +513,7 @@ static curl_off_t encoder_base64_size(curl_mimepart *part) * Check if a CRLF or end of data is in input buffer at current position + n. * Return -1 if more data needed, 1 if CRLF or end of data, else 0. */ -static int qp_lookahead_eol(mime_encoder_state *st, int ateof, size_t n) +static int qp_lookahead_eol(struct mime_encoder_state *st, int ateof, size_t n) { n += st->bufbeg; if(n >= st->bufend && ateof) @@ -502,7 +530,7 @@ static int qp_lookahead_eol(mime_encoder_state *st, int ateof, size_t n) static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, curl_mimepart *part) { - mime_encoder_state *st = &part->encstate; + struct mime_encoder_state *st = &part->encstate; char *ptr = buffer; size_t cursize = 0; int softlinebreak; @@ -567,7 +595,6 @@ static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, switch(qp_lookahead_eol(st, ateof, consumed)) { case -1: /* Need more data. */ return cursize; - break; case 0: /* Not followed by a CRLF. */ softlinebreak = 1; break; @@ -581,8 +608,11 @@ static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, } /* If the output buffer would overflow, do not store. */ - if(len > size) + if(len > size) { + if(!cursize) + return STOP_FILLING; break; + } /* Append to output buffer. */ memcpy(ptr, buf, len); @@ -612,16 +642,18 @@ static size_t mime_mem_read(char *buffer, size_t size, size_t nitems, void *instream) { curl_mimepart *part = (curl_mimepart *) instream; - size_t sz = (size_t) part->datasize - part->state.offset; + size_t sz = curlx_sotouz(part->datasize - part->state.offset); (void) size; /* Always 1.*/ + if(!nitems) + return STOP_FILLING; + if(sz > nitems) sz = nitems; if(sz) - memcpy(buffer, (char *) &part->data[part->state.offset], sz); + memcpy(buffer, part->data + curlx_sotouz(part->state.offset), sz); - part->state.offset += sz; return sz; } @@ -641,7 +673,7 @@ static int mime_mem_seek(void *instream, curl_off_t offset, int whence) if(offset < 0 || offset > part->datasize) return CURL_SEEKFUNC_FAIL; - part->state.offset = (size_t) offset; + part->state.offset = offset; return CURL_SEEKFUNC_OK; } @@ -653,7 +685,7 @@ static void mime_mem_free(void *ptr) /* Named file callbacks. */ /* Argument is a pointer to the mime part. */ -static int mime_open_file(curl_mimepart * part) +static int mime_open_file(curl_mimepart *part) { /* Open a MIMEKIND_FILE part. */ @@ -668,6 +700,9 @@ static size_t mime_file_read(char *buffer, size_t size, size_t nitems, { curl_mimepart *part = (curl_mimepart *) instream; + if(!nitems) + return STOP_FILLING; + if(mime_open_file(part)) return READ_ERROR; @@ -705,21 +740,22 @@ static void mime_file_free(void *ptr) /* Argument is a pointer to the mime structure. */ /* Readback a byte string segment. */ -static size_t readback_bytes(mime_state *state, +static size_t readback_bytes(struct mime_state *state, char *buffer, size_t bufsize, const char *bytes, size_t numbytes, const char *trail) { size_t sz; + size_t offset = curlx_sotouz(state->offset); - if(numbytes > state->offset) { - sz = numbytes - state->offset; - bytes += state->offset; + if(numbytes > offset) { + sz = numbytes - offset; + bytes += offset; } else { size_t tsz = strlen(trail); - sz = state->offset - numbytes; + sz = offset - numbytes; if(sz >= tsz) return 0; bytes = trail + sz; @@ -736,25 +772,79 @@ static size_t readback_bytes(mime_state *state, /* Read a non-encoded part content. */ static size_t read_part_content(curl_mimepart *part, - char *buffer, size_t bufsize) + char *buffer, size_t bufsize, bool *hasread) { size_t sz = 0; - if(part->readfunc) - sz = part->readfunc(buffer, 1, bufsize, part->arg); + switch(part->lastreadstatus) { + case 0: + case CURL_READFUNC_ABORT: + case CURL_READFUNC_PAUSE: + case READ_ERROR: + return part->lastreadstatus; + default: + break; + } + + /* If we can determine we are at end of part data, spare a read. */ + if(part->datasize != (curl_off_t) -1 && + part->state.offset >= part->datasize) { + /* sz is already zero. */ + } + else { + switch(part->kind) { + case MIMEKIND_MULTIPART: + /* + * Cannot be processed as other kinds since read function requires + * an additional parameter and is highly recursive. + */ + sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread); + break; + case MIMEKIND_FILE: + if(part->fp && feof(part->fp)) + break; /* At EOF. */ + /* FALLTHROUGH */ + default: + if(part->readfunc) { + if(!(part->flags & MIME_FAST_READ)) { + if(*hasread) + return STOP_FILLING; + *hasread = TRUE; + } + sz = part->readfunc(buffer, 1, bufsize, part->arg); + } + break; + } + } + + switch(sz) { + case STOP_FILLING: + break; + case 0: + case CURL_READFUNC_ABORT: + case CURL_READFUNC_PAUSE: + case READ_ERROR: + part->lastreadstatus = sz; + break; + default: + part->state.offset += sz; + part->lastreadstatus = sz; + break; + } + return sz; } /* Read and encode part content. */ -static size_t read_encoded_part_content(curl_mimepart *part, - char *buffer, size_t bufsize) +static size_t read_encoded_part_content(curl_mimepart *part, char *buffer, + size_t bufsize, bool *hasread) { - mime_encoder_state *st = &part->encstate; + struct mime_encoder_state *st = &part->encstate; size_t cursize = 0; size_t sz; bool ateof = FALSE; - while(bufsize) { + for(;;) { if(st->bufbeg < st->bufend || ateof) { /* Encode buffered data. */ sz = part->encoder->encodefunc(buffer, bufsize, ateof, part); @@ -763,9 +853,8 @@ static size_t read_encoded_part_content(curl_mimepart *part, if(ateof) return cursize; break; - case CURL_READFUNC_ABORT: - case CURL_READFUNC_PAUSE: case READ_ERROR: + case STOP_FILLING: return cursize? cursize: sz; default: cursize += sz; @@ -787,7 +876,7 @@ static size_t read_encoded_part_content(curl_mimepart *part, 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); + sizeof(st->buf) - st->bufend, hasread); switch(sz) { case 0: ateof = TRUE; @@ -795,6 +884,7 @@ static size_t read_encoded_part_content(curl_mimepart *part, case CURL_READFUNC_ABORT: case CURL_READFUNC_PAUSE: case READ_ERROR: + case STOP_FILLING: return cursize? cursize: sz; default: st->bufend += sz; @@ -802,12 +892,12 @@ static size_t read_encoded_part_content(curl_mimepart *part, } } - return cursize; + /* NOTREACHED */ } /* Readback a mime part. */ static size_t readback_part(curl_mimepart *part, - char *buffer, size_t bufsize) + char *buffer, size_t bufsize, bool *hasread) { size_t cursize = 0; #ifdef CURL_DOES_CONVERSIONS @@ -866,9 +956,9 @@ static size_t readback_part(curl_mimepart *part, break; case MIMESTATE_CONTENT: if(part->encoder) - sz = read_encoded_part_content(part, buffer, bufsize); + sz = read_encoded_part_content(part, buffer, bufsize, hasread); else - sz = read_part_content(part, buffer, bufsize); + sz = read_part_content(part, buffer, bufsize, hasread); switch(sz) { case 0: mimesetstate(&part->state, MIMESTATE_END, NULL); @@ -881,6 +971,7 @@ static size_t readback_part(curl_mimepart *part, case CURL_READFUNC_ABORT: case CURL_READFUNC_PAUSE: case READ_ERROR: + case STOP_FILLING: return cursize? cursize: sz; } break; @@ -909,9 +1000,9 @@ static size_t readback_part(curl_mimepart *part, return cursize; } -/* Readback from mime. */ +/* 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) + void *instream, bool *hasread) { curl_mime *mime = (curl_mime *) instream; size_t cursize = 0; @@ -932,7 +1023,7 @@ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, #endif mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, mime->firstpart); /* The first boundary always follows the header termination empty line, - so is always preceded by a CRLK. We can then spare 2 characters + so is always preceded by a CRLF. We can then spare 2 characters by skipping the leading CRLF in boundary. */ mime->state.offset += 2; break; @@ -962,11 +1053,12 @@ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, mimesetstate(&mime->state, MIMESTATE_END, NULL); break; } - sz = readback_part(part, buffer, nitems); + sz = readback_part(part, buffer, nitems, hasread); switch(sz) { case CURL_READFUNC_ABORT: case CURL_READFUNC_PAUSE: case READ_ERROR: + case STOP_FILLING: return cursize? cursize: sz; case 0: #ifdef CURL_DOES_CONVERSIONS @@ -1031,6 +1123,7 @@ static int mime_part_rewind(curl_mimepart *part) if(res == CURL_SEEKFUNC_OK) mimesetstate(&part->state, targetstate, NULL); + part->lastreadstatus = 1; /* Successful read status. */ return res; } @@ -1073,6 +1166,8 @@ static void cleanup_part_content(curl_mimepart *part) part->datasize = (curl_off_t) 0; /* No size yet. */ cleanup_encoder_state(&part->encstate); part->kind = MIMEKIND_NONE; + part->flags &= ~MIME_FAST_READ; + part->lastreadstatus = 1; /* Successful read status. */ } static void mime_subparts_free(void *ptr) @@ -1135,6 +1230,8 @@ CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src) const curl_mimepart *s; CURLcode res = CURLE_OK; + DEBUGASSERT(dst); + /* Duplicate content. */ switch(src->kind) { case MIMEKIND_NONE: @@ -1184,20 +1281,18 @@ CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src) } } - /* Duplicate other fields. */ - if(dst != NULL) + if(!res) { + /* Duplicate other fields. */ dst->encoder = src->encoder; - else - res = CURLE_WRITE_ERROR; - if(!res) res = curl_mime_type(dst, src->mimetype); + } if(!res) res = curl_mime_name(dst, src->name); if(!res) res = curl_mime_filename(dst, src->filename); /* If an error occurred, rollback. */ - if(res && dst) + if(res) Curl_mime_cleanpart(dst); return res; @@ -1238,6 +1333,7 @@ void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy) { memset((char *) part, 0, sizeof(*part)); part->easy = easy; + part->lastreadstatus = 1; /* Successful read status. */ mimesetstate(&part->state, MIMESTATE_BEGIN, NULL); } @@ -1323,11 +1419,12 @@ CURLcode curl_mime_data(curl_mimepart *part, if(datasize) memcpy(part->data, data, datasize); - part->data[datasize] = '\0'; /* Set a nul terminator as sentinel. */ + part->data[datasize] = '\0'; /* Set a null terminator as sentinel. */ part->readfunc = mime_mem_read; part->seekfunc = mime_mem_seek; part->freefunc = mime_mem_free; + part->flags |= MIME_FAST_READ; part->kind = MIMEKIND_DATA; } @@ -1405,7 +1502,7 @@ CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype) CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding) { CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; - const mime_encoder *mep; + const struct mime_encoder *mep; if(!part) return result; @@ -1502,7 +1599,7 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part, } subparts->parent = part; - part->readfunc = mime_subparts_read; + /* Subparts are processed internally: no read callback. */ part->seekfunc = mime_subparts_seek; part->freefunc = take_ownership? mime_subparts_free: mime_subparts_unbind; part->arg = subparts; @@ -1524,9 +1621,23 @@ CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts) size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream) { curl_mimepart *part = (curl_mimepart *) instream; + size_t ret; + bool hasread; (void) size; /* Always 1. */ - return readback_part(part, buffer, nitems); + + do { + hasread = FALSE; + ret = readback_part(part, buffer, nitems, &hasread); + /* + * 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 + * deliver a new bunch for the few data accumulated so far), force another + * read until we get enough data or a special exit code. + */ + } while(ret == STOP_FILLING); + + return ret; } /* Rewind mime stream. */ @@ -1667,6 +1778,23 @@ const char *Curl_mime_contenttype(const char *filename) return NULL; } +static bool content_type_match(const char *contenttype, const char *target) +{ + size_t len = strlen(target); + + if(contenttype && strncasecompare(contenttype, target, len)) + switch(contenttype[len]) { + case '\0': + case '\t': + case '\r': + case '\n': + case ' ': + case ';': + return TRUE; + } + return FALSE; +} + CURLcode Curl_mime_prepare_headers(curl_mimepart *part, const char *contenttype, const char *disposition, @@ -1718,7 +1846,7 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, boundary = mime->boundary; } else if(contenttype && !customct && - strcasecompare(contenttype, "text/plain")) + content_type_match(contenttype, "text/plain")) if(strategy == MIMESTRATEGY_MAIL || !part->filename) contenttype = NULL; @@ -1794,7 +1922,7 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, curl_mimepart *subpart; disposition = NULL; - if(strcasecompare(contenttype, "multipart/form-data")) + if(content_type_match(contenttype, "multipart/form-data")) disposition = "form-data"; for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) { ret = Curl_mime_prepare_headers(subpart, NULL, disposition, strategy); @@ -1805,6 +1933,26 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, return ret; } +/* Recursively reset paused status in the given part. */ +void Curl_mime_unpause(curl_mimepart *part) +{ + if(part) { + if(part->lastreadstatus == CURL_READFUNC_PAUSE) + part->lastreadstatus = 1; /* Successful read status. */ + if(part->kind == MIMEKIND_MULTIPART) { + curl_mime *mime = (curl_mime *) part->arg; + + if(mime) { + curl_mimepart *subpart; + + for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) + Curl_mime_unpause(subpart); + } + } + } +} + + #else /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */ /* Mime not compiled in: define stubs for externally-referenced functions. */ @@ -1901,4 +2049,11 @@ CURLcode curl_mime_headers(curl_mimepart *part, return CURLE_NOT_BUILT_IN; } +CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...) +{ + (void)slp; + (void)fmt; + return CURLE_NOT_BUILT_IN; +} + #endif /* if disabled */ diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h index 4c9a5fb71f4..50b7ea6b066 100644 --- a/Utilities/cmcurl/lib/mime.h +++ b/Utilities/cmcurl/lib/mime.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,6 +31,7 @@ /* Part flags. */ #define MIME_USERHEADERS_OWNER (1 << 0) #define MIME_BODY_ONLY (1 << 1) +#define MIME_FAST_READ (1 << 2) #define FILE_CONTENTTYPE_DEFAULT "application/octet-stream" #define MULTIPART_CONTENTTYPE_DEFAULT "multipart/mixed" @@ -68,43 +69,43 @@ enum mimestrategy { }; /* Content transfer encoder. */ -typedef struct { +struct mime_encoder { const char * name; /* Encoding name. */ size_t (*encodefunc)(char *buffer, size_t size, bool ateof, curl_mimepart *part); /* Encoded read. */ curl_off_t (*sizefunc)(curl_mimepart *part); /* Encoded size. */ -} mime_encoder; +}; /* Content transfer encoder state. */ -typedef struct { +struct mime_encoder_state { size_t pos; /* Position on output line. */ size_t bufbeg; /* Next data index in input buffer. */ size_t bufend; /* First unused byte index in input buffer. */ char buf[ENCODING_BUFFER_SIZE]; /* Input buffer. */ -} mime_encoder_state; +}; /* Mime readback state. */ -typedef struct { +struct mime_state { enum mimestate state; /* Current state token. */ void *ptr; /* State-dependent pointer. */ - size_t offset; /* State-dependent offset. */ -} mime_state; + curl_off_t offset; /* State-dependent offset. */ +}; /* minimum buffer size for the boundary string */ #define MIME_BOUNDARY_LEN (24 + MIME_RAND_BOUNDARY_CHARS + 1) /* A mime multipart. */ -struct curl_mime_s { +struct curl_mime { struct Curl_easy *easy; /* The associated easy handle. */ curl_mimepart *parent; /* Parent part. */ curl_mimepart *firstpart; /* First part. */ curl_mimepart *lastpart; /* Last part. */ char boundary[MIME_BOUNDARY_LEN]; /* The part boundary. */ - mime_state state; /* Current readback state. */ + struct mime_state state; /* Current readback state. */ }; /* A mime part. */ -struct curl_mimepart_s { +struct curl_mimepart { struct Curl_easy *easy; /* The associated easy handle. */ curl_mime *parent; /* Parent mime structure. */ curl_mimepart *nextpart; /* Forward linked list. */ @@ -122,30 +123,35 @@ struct curl_mimepart_s { char *name; /* Data name. */ curl_off_t datasize; /* Expected data size. */ unsigned int flags; /* Flags. */ - mime_state state; /* Current readback state. */ - const mime_encoder *encoder; /* Content data encoder. */ - mime_encoder_state encstate; /* Data encoder state. */ + struct mime_state state; /* Current readback state. */ + const struct mime_encoder *encoder; /* Content data encoder. */ + struct mime_encoder_state encstate; /* Data encoder state. */ + size_t lastreadstatus; /* Last read callback returned status. */ }; -#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \ +CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...); + +#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \ !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP) /* Prototypes. */ -void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy); -void Curl_mime_cleanpart(curl_mimepart *part); -CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src); -CURLcode Curl_mime_set_subparts(curl_mimepart *part, - curl_mime *subparts, int take_ownership); -CURLcode Curl_mime_prepare_headers(curl_mimepart *part, +void Curl_mime_initpart(struct curl_mimepart *part, struct Curl_easy *easy); +void Curl_mime_cleanpart(struct curl_mimepart *part); +CURLcode Curl_mime_duppart(struct curl_mimepart *dst, + const curl_mimepart *src); +CURLcode Curl_mime_set_subparts(struct curl_mimepart *part, + struct curl_mime *subparts, + int take_ownership); +CURLcode Curl_mime_prepare_headers(struct curl_mimepart *part, const char *contenttype, const char *disposition, enum mimestrategy strategy); -curl_off_t Curl_mime_size(curl_mimepart *part); +curl_off_t Curl_mime_size(struct curl_mimepart *part); size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream); -CURLcode Curl_mime_rewind(curl_mimepart *part); -CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...); +CURLcode Curl_mime_rewind(struct curl_mimepart *part); const char *Curl_mime_contenttype(const char *filename); +void Curl_mime_unpause(struct curl_mimepart *part); #else /* if disabled */ @@ -157,7 +163,7 @@ const char *Curl_mime_contenttype(const char *filename); #define Curl_mime_size(x) (curl_off_t) -1 #define Curl_mime_read NULL #define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN) -#define Curl_mime_add_header(x,y,...) CURLE_NOT_BUILT_IN +#define Curl_mime_unpause(x) #endif diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c index e1909367824..63c9d11a5af 100644 --- a/Utilities/cmcurl/lib/mprintf.c +++ b/Utilities/cmcurl/lib/mprintf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1999 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1999 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -36,6 +36,7 @@ */ #include "curl_setup.h" +#include "dynbuf.h" #include #include "curl_memory.h" @@ -104,7 +105,7 @@ static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; done++; \ else \ return done; /* return immediately on failure */ \ - } WHILE_FALSE + } while(0) /* Data type to read from the arglist */ typedef enum { @@ -145,7 +146,7 @@ enum { FLAGS_FLOATG = 1<<19 /* %g or %G */ }; -typedef struct { +struct va_stack { FormatType type; int flags; long width; /* width OR width parameter number */ @@ -159,7 +160,7 @@ typedef struct { } num; double dnum; } data; -} va_stack_t; +}; struct nsprintf { char *buffer; @@ -168,11 +169,9 @@ struct nsprintf { }; struct asprintf { - char *buffer; /* allocated buffer */ - size_t len; /* length of string */ - size_t alloc; /* length of alloc */ - int fail; /* (!= 0) if an alloc has failed and thus - the output is not the complete data */ + struct dynbuf b; + bool fail; /* if an alloc has failed and thus the output is not the complete + data */ }; static long dprintf_DollarString(char *input, char **end) @@ -224,8 +223,8 @@ static bool dprintf_IsQualifierNoDollar(const char *fmt) * ******************************************************************/ -static int dprintf_Pass1(const char *format, va_stack_t *vto, char **endpos, - va_list arglist) +static int dprintf_Pass1(const char *format, struct va_stack *vto, + char **endpos, va_list arglist) { char *fmt = (char *)format; int param_num = 0; @@ -571,13 +570,11 @@ static int dprintf_formatf( long param; /* current parameter to read */ long param_num = 0; /* parameter counter */ - va_stack_t vto[MAX_PARAMETERS]; + struct va_stack vto[MAX_PARAMETERS]; char *endpos[MAX_PARAMETERS]; char **end; - char work[BUFFSIZE]; - - va_stack_t *p; + struct va_stack *p; /* 'workend' points to the final buffer byte position, but with an extra byte as margin to avoid the (false?) warning Coverity gives us @@ -1031,35 +1028,10 @@ static int alloc_addbyter(int output, FILE *data) struct asprintf *infop = (struct asprintf *)data; unsigned char outc = (unsigned char)output; - if(!infop->buffer) { - infop->buffer = malloc(32); - if(!infop->buffer) { - infop->fail = 1; - return -1; /* fail */ - } - infop->alloc = 32; - infop->len = 0; - } - else if(infop->len + 1 >= infop->alloc) { - char *newptr = NULL; - size_t newsize = infop->alloc*2; - - /* detect wrap-around or other overflow problems */ - if(newsize > infop->alloc) - newptr = realloc(infop->buffer, newsize); - - if(!newptr) { - infop->fail = 1; - return -1; /* fail */ - } - infop->buffer = newptr; - infop->alloc = newsize; + if(Curl_dyn_addn(&infop->b, &outc, 1)) { + infop->fail = 1; + return -1; /* fail */ } - - infop->buffer[ infop->len ] = outc; - - infop->len++; - return outc; /* fputc() returns like this on success */ } @@ -1068,24 +1040,18 @@ char *curl_maprintf(const char *format, ...) va_list ap_save; /* argument pointer */ int retcode; struct asprintf info; - - info.buffer = NULL; - info.len = 0; - info.alloc = 0; + Curl_dyn_init(&info.b, DYN_APRINTF); info.fail = 0; va_start(ap_save, format); retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save); va_end(ap_save); if((-1 == retcode) || info.fail) { - if(info.alloc) - free(info.buffer); + Curl_dyn_free(&info.b); return NULL; } - if(info.alloc) { - info.buffer[info.len] = 0; /* we terminate this with a zero byte */ - return info.buffer; - } + if(Curl_dyn_len(&info.b)) + return Curl_dyn_ptr(&info.b); return strdup(""); } @@ -1093,23 +1059,16 @@ char *curl_mvaprintf(const char *format, va_list ap_save) { int retcode; struct asprintf info; - - info.buffer = NULL; - info.len = 0; - info.alloc = 0; + Curl_dyn_init(&info.b, DYN_APRINTF); info.fail = 0; retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save); if((-1 == retcode) || info.fail) { - if(info.alloc) - free(info.buffer); + Curl_dyn_free(&info.b); return NULL; } - - if(info.alloc) { - info.buffer[info.len] = 0; /* we terminate this with a zero byte */ - return info.buffer; - } + if(Curl_dyn_len(&info.b)) + return Curl_dyn_ptr(&info.b); return strdup(""); } diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c new file mode 100644 index 00000000000..f6f44161405 --- /dev/null +++ b/Utilities/cmcurl/lib/mqtt.c @@ -0,0 +1,628 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2020, Daniel Stenberg, , et al. + * Copyright (C) 2019, Björn Stenberg, + * + * 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.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef CURL_ENABLE_MQTT + +#include "urldata.h" +#include +#include "transfer.h" +#include "sendf.h" +#include "progress.h" +#include "mqtt.h" +#include "select.h" +#include "strdup.h" +#include "url.h" +#include "escape.h" +#include "warnless.h" +#include "curl_printf.h" +#include "curl_memory.h" +#include "multiif.h" +#include "rand.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +#define MQTT_MSG_CONNECT 0x10 +#define MQTT_MSG_CONNACK 0x20 +#define MQTT_MSG_PUBLISH 0x30 +#define MQTT_MSG_SUBSCRIBE 0x82 +#define MQTT_MSG_SUBACK 0x90 +#define MQTT_MSG_DISCONNECT 0xe0 + +#define MQTT_CONNACK_LEN 2 +#define MQTT_SUBACK_LEN 3 +#define MQTT_CLIENTID_LEN 12 /* "curl0123abcd" */ + +/* + * Forward declarations. + */ + +static CURLcode mqtt_do(struct connectdata *conn, bool *done); +static CURLcode mqtt_doing(struct connectdata *conn, bool *done); +static int mqtt_getsock(struct connectdata *conn, curl_socket_t *sock); +static CURLcode mqtt_setup_conn(struct connectdata *conn); + +/* + * MQTT protocol handler. + */ + +const struct Curl_handler Curl_handler_mqtt = { + "MQTT", /* scheme */ + mqtt_setup_conn, /* setup_connection */ + mqtt_do, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + mqtt_doing, /* doing */ + ZERO_NULL, /* proto_getsock */ + mqtt_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ + PORT_MQTT, /* defport */ + CURLPROTO_MQTT, /* protocol */ + PROTOPT_NONE /* flags */ +}; + +static CURLcode mqtt_setup_conn(struct connectdata *conn) +{ + /* allocate the HTTP-specific struct for the Curl_easy, only to survive + during this request */ + struct MQTT *mq; + struct Curl_easy *data = conn->data; + DEBUGASSERT(data->req.protop == NULL); + + mq = calloc(1, sizeof(struct MQTT)); + if(!mq) + return CURLE_OUT_OF_MEMORY; + data->req.protop = mq; + return CURLE_OK; +} + +static CURLcode mqtt_send(struct connectdata *conn, + char *buf, size_t len) +{ + CURLcode result = CURLE_OK; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + struct Curl_easy *data = conn->data; + struct MQTT *mq = data->req.protop; + ssize_t n; + result = Curl_write(conn, sockfd, buf, len, &n); + if(!result && data->set.verbose) + Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); + if(len != (size_t)n) { + size_t nsend = len - n; + char *sendleftovers = Curl_memdup(&buf[n], nsend); + if(!sendleftovers) + return CURLE_OUT_OF_MEMORY; + mq->sendleftovers = sendleftovers; + mq->nsend = nsend; + } + return result; +} + +/* Generic function called by the multi interface to figure out what socket(s) + to wait for and for what actions during the DOING and PROTOCONNECT + states */ +static int mqtt_getsock(struct connectdata *conn, + curl_socket_t *sock) +{ + sock[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_READSOCK(FIRSTSOCKET); +} + +static CURLcode mqtt_connect(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + const size_t client_id_offset = 14; + const size_t packetlen = client_id_offset + MQTT_CLIENTID_LEN; + char client_id[MQTT_CLIENTID_LEN + 1] = "curl"; + const size_t curl_len = strlen("curl"); + char packet[32] = { + MQTT_MSG_CONNECT, /* packet type */ + 0x00, /* remaining length */ + 0x00, 0x04, /* protocol length */ + 'M','Q','T','T', /* protocol name */ + 0x04, /* protocol level */ + 0x02, /* CONNECT flag: CleanSession */ + 0x00, 0x3c, /* keep-alive 0 = disabled */ + 0x00, 0x00 /* payload1 length */ + }; + packet[1] = (packetlen - 2) & 0x7f; + packet[client_id_offset - 1] = MQTT_CLIENTID_LEN; + + result = Curl_rand_hex(conn->data, (unsigned char *)&client_id[curl_len], + MQTT_CLIENTID_LEN - curl_len + 1); + memcpy(&packet[client_id_offset], client_id, MQTT_CLIENTID_LEN); + infof(conn->data, "Using client id '%s'\n", client_id); + if(!result) + result = mqtt_send(conn, packet, packetlen); + return result; +} + +static CURLcode mqtt_disconnect(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + result = mqtt_send(conn, (char *)"\xe0\x00", 2); + return result; +} + +static CURLcode mqtt_verify_connack(struct connectdata *conn) +{ + CURLcode result; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + unsigned char readbuf[MQTT_CONNACK_LEN]; + ssize_t nread; + struct Curl_easy *data = conn->data; + + result = Curl_read(conn, sockfd, (char *)readbuf, MQTT_CONNACK_LEN, &nread); + if(result) + goto fail; + + if(data->set.verbose) + Curl_debug(data, CURLINFO_HEADER_IN, (char *)readbuf, (size_t)nread); + + /* fixme */ + if(nread < MQTT_CONNACK_LEN) { + result = CURLE_WEIRD_SERVER_REPLY; + goto fail; + } + + /* verify CONNACK */ + if(readbuf[0] != 0x00 || readbuf[1] != 0x00) { + failf(data, "Expected %02x%02x but got %02x%02x", + 0x00, 0x00, readbuf[0], readbuf[1]); + result = CURLE_WEIRD_SERVER_REPLY; + } + +fail: + return result; +} + +static CURLcode mqtt_get_topic(struct connectdata *conn, + char **topic, size_t *topiclen) +{ + CURLcode result = CURLE_OK; + char *path = conn->data->state.up.path; + + if(strlen(path) > 1) { + result = Curl_urldecode(conn->data, path + 1, 0, topic, topiclen, + REJECT_NADA); + } + else { + failf(conn->data, "Error: No topic specified."); + result = CURLE_URL_MALFORMAT; + } + return result; +} + + +static int mqtt_encode_len(char *buf, size_t len) +{ + unsigned char encoded; + int i; + + for(i = 0; (len > 0) && (i<4); i++) { + encoded = len % 0x80; + len /= 0x80; + if(len) + encoded |= 0x80; + buf[i] = encoded; + } + + return i; +} + +static CURLcode mqtt_subscribe(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + char *topic = NULL; + size_t topiclen; + unsigned char *packet = NULL; + size_t packetlen; + char encodedsize[4]; + size_t n; + + result = mqtt_get_topic(conn, &topic, &topiclen); + if(result) + goto fail; + + conn->proto.mqtt.packetid++; + + packetlen = topiclen + 5; /* packetid + topic (has a two byte length field) + + 2 bytes topic length + QoS byte */ + n = mqtt_encode_len((char *)encodedsize, packetlen); + packetlen += n + 1; /* add one for the control packet type byte */ + + packet = malloc(packetlen); + if(!packet) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + packet[0] = MQTT_MSG_SUBSCRIBE; + memcpy(&packet[1], encodedsize, n); + packet[1 + n] = (conn->proto.mqtt.packetid >> 8) & 0xff; + packet[2 + n] = conn->proto.mqtt.packetid & 0xff; + packet[3 + n] = (topiclen >> 8) & 0xff; + packet[4 + n ] = topiclen & 0xff; + memcpy(&packet[5 + n], topic, topiclen); + packet[5 + n + topiclen] = 0; /* QoS zero */ + + result = mqtt_send(conn, (char *)packet, packetlen); + +fail: + free(topic); + free(packet); + return result; +} + +/* + * Called when the first byte was already read. + */ +static CURLcode mqtt_verify_suback(struct connectdata *conn) +{ + CURLcode result; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + unsigned char readbuf[MQTT_SUBACK_LEN]; + ssize_t nread; + struct mqtt_conn *mqtt = &conn->proto.mqtt; + + result = Curl_read(conn, sockfd, (char *)readbuf, MQTT_SUBACK_LEN, &nread); + if(result) + goto fail; + + if(conn->data->set.verbose) + Curl_debug(conn->data, CURLINFO_HEADER_IN, (char *)readbuf, (size_t)nread); + + /* fixme */ + if(nread < MQTT_SUBACK_LEN) { + result = CURLE_WEIRD_SERVER_REPLY; + goto fail; + } + + /* verify SUBACK */ + if(readbuf[0] != ((mqtt->packetid >> 8) & 0xff) || + readbuf[1] != (mqtt->packetid & 0xff) || + readbuf[2] != 0x00) + result = CURLE_WEIRD_SERVER_REPLY; + +fail: + return result; +} + +static CURLcode mqtt_publish(struct connectdata *conn) +{ + CURLcode result; + char *payload = conn->data->set.postfields; + size_t payloadlen = (size_t)conn->data->set.postfieldsize; + char *topic = NULL; + size_t topiclen; + unsigned char *pkt = NULL; + size_t i = 0; + size_t remaininglength; + size_t encodelen; + char encodedbytes[4]; + + result = mqtt_get_topic(conn, &topic, &topiclen); + if(result) + goto fail; + + remaininglength = payloadlen + 2 + topiclen; + encodelen = mqtt_encode_len(encodedbytes, remaininglength); + + /* add the control byte and the encoded remaining length */ + pkt = malloc(remaininglength + 1 + encodelen); + if(!pkt) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + /* assemble packet */ + pkt[i++] = MQTT_MSG_PUBLISH; + memcpy(&pkt[i], encodedbytes, encodelen); + i += encodelen; + pkt[i++] = (topiclen >> 8) & 0xff; + pkt[i++] = (topiclen & 0xff); + memcpy(&pkt[i], topic, topiclen); + i += topiclen; + memcpy(&pkt[i], payload, payloadlen); + i += payloadlen; + result = mqtt_send(conn, (char *)pkt, i); + +fail: + free(pkt); + free(topic); + return result; +} + +static size_t mqtt_decode_len(unsigned char *buf, + size_t buflen, size_t *lenbytes) +{ + size_t len = 0; + size_t mult = 1; + size_t i; + unsigned char encoded = 128; + + for(i = 0; (i < buflen) && (encoded & 128); i++) { + encoded = buf[i]; + len += (encoded & 127) * mult; + mult *= 128; + } + + if(lenbytes) + *lenbytes = i; + + return len; +} + +#ifdef CURLDEBUG +static const char *statenames[]={ + "MQTT_FIRST", + "MQTT_REMAINING_LENGTH", + "MQTT_CONNACK", + "MQTT_SUBACK", + "MQTT_SUBACK_COMING", + "MQTT_PUBWAIT", + "MQTT_PUB_REMAIN", + + "NOT A STATE" +}; +#endif + +/* The only way to change state */ +static void mqstate(struct connectdata *conn, + enum mqttstate state, + enum mqttstate nextstate) /* used if state == FIRST */ +{ + struct mqtt_conn *mqtt = &conn->proto.mqtt; +#ifdef CURLDEBUG + infof(conn->data, "%s (from %s) (next is %s)\n", + statenames[state], + statenames[mqtt->state], + (state == MQTT_FIRST)? statenames[nextstate] : ""); +#endif + mqtt->state = state; + if(state == MQTT_FIRST) + mqtt->nextstate = nextstate; +} + + +/* for the publish packet */ +#define MQTT_HEADER_LEN 5 /* max 5 bytes */ + +static CURLcode mqtt_read_publish(struct connectdata *conn, + bool *done) +{ + CURLcode result = CURLE_OK; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + ssize_t nread; + struct Curl_easy *data = conn->data; + unsigned char *pkt = (unsigned char *)data->state.buffer; + size_t remlen; + struct mqtt_conn *mqtt = &conn->proto.mqtt; + struct MQTT *mq = data->req.protop; + unsigned char packet; + + switch(mqtt->state) { + MQTT_SUBACK_COMING: + case MQTT_SUBACK_COMING: + result = mqtt_verify_suback(conn); + if(result) + break; + + mqstate(conn, MQTT_FIRST, MQTT_PUBWAIT); + break; + + case MQTT_SUBACK: + case MQTT_PUBWAIT: + /* we are expecting PUBLISH or SUBACK */ + packet = mq->firstbyte & 0xf0; + if(packet == MQTT_MSG_PUBLISH) + mqstate(conn, MQTT_PUB_REMAIN, MQTT_NOSTATE); + else if(packet == MQTT_MSG_SUBACK) { + mqstate(conn, MQTT_SUBACK_COMING, MQTT_NOSTATE); + goto MQTT_SUBACK_COMING; + } + else if(packet == MQTT_MSG_DISCONNECT) { + infof(data, "Got DISCONNECT\n"); + *done = TRUE; + goto end; + } + else { + result = CURLE_WEIRD_SERVER_REPLY; + goto end; + } + + /* -- switched state -- */ + remlen = mq->remaining_length; + infof(data, "Remaining length: %zd bytes\n", remlen); + Curl_pgrsSetDownloadSize(data, remlen); + data->req.bytecount = 0; + data->req.size = remlen; + mq->npacket = remlen; /* get this many bytes */ + /* FALLTHROUGH */ + case MQTT_PUB_REMAIN: { + /* read rest of packet, but no more. Cap to buffer size */ + struct SingleRequest *k = &data->req; + size_t rest = mq->npacket; + if(rest > (size_t)data->set.buffer_size) + rest = (size_t)data->set.buffer_size; + result = Curl_read(conn, sockfd, (char *)pkt, rest, &nread); + if(result) { + if(CURLE_AGAIN == result) { + infof(data, "EEEE AAAAGAIN\n"); + } + goto end; + } + if(!nread) { + infof(data, "server disconnected\n"); + result = CURLE_PARTIAL_FILE; + goto end; + } + if(data->set.verbose) + Curl_debug(data, CURLINFO_DATA_IN, (char *)pkt, (size_t)nread); + + mq->npacket -= nread; + k->bytecount += nread; + Curl_pgrsSetDownloadCounter(data, k->bytecount); + + /* if QoS is set, message contains packet id */ + + result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)pkt, nread); + if(result) + goto end; + + if(!mq->npacket) + /* no more PUBLISH payload, back to subscribe wait state */ + mqstate(conn, MQTT_FIRST, MQTT_PUBWAIT); + break; + } + default: + DEBUGASSERT(NULL); /* illegal state */ + result = CURLE_WEIRD_SERVER_REPLY; + goto end; + } + end: + return result; +} + +static CURLcode mqtt_do(struct connectdata *conn, bool *done) +{ + CURLcode result = CURLE_OK; + struct Curl_easy *data = conn->data; + + *done = FALSE; /* unconditionally */ + + result = mqtt_connect(conn); + if(result) { + failf(data, "Error %d sending MQTT CONN request", result); + return result; + } + mqstate(conn, MQTT_FIRST, MQTT_CONNACK); + return CURLE_OK; +} + +static CURLcode mqtt_doing(struct connectdata *conn, bool *done) +{ + CURLcode result = CURLE_OK; + struct mqtt_conn *mqtt = &conn->proto.mqtt; + struct Curl_easy *data = conn->data; + struct MQTT *mq = data->req.protop; + ssize_t nread; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + unsigned char *pkt = (unsigned char *)data->state.buffer; + unsigned char byte; + + *done = FALSE; + + if(mq->nsend) { + /* send the remainder of an outgoing packet */ + char *ptr = mq->sendleftovers; + result = mqtt_send(conn, mq->sendleftovers, mq->nsend); + free(ptr); + if(result) + return result; + } + + infof(data, "mqtt_doing: state [%d]\n", (int) mqtt->state); + switch(mqtt->state) { + case MQTT_FIRST: + /* Read the initial byte only */ + result = Curl_read(conn, sockfd, (char *)&mq->firstbyte, 1, &nread); + if(result) + break; + if(data->set.verbose) + Curl_debug(data, CURLINFO_HEADER_IN, (char *)&mq->firstbyte, 1); + /* remember the first byte */ + mq->npacket = 0; + mqstate(conn, MQTT_REMAINING_LENGTH, MQTT_NOSTATE); + /* FALLTHROUGH */ + case MQTT_REMAINING_LENGTH: + do { + result = Curl_read(conn, sockfd, (char *)&byte, 1, &nread); + if(result) + break; + if(data->set.verbose) + Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1); + pkt[mq->npacket++] = byte; + } while((byte & 0x80) && (mq->npacket < 4)); + if(result) + break; + mq->remaining_length = mqtt_decode_len(&pkt[0], mq->npacket, NULL); + mq->npacket = 0; + if(mq->remaining_length) { + mqstate(conn, mqtt->nextstate, MQTT_NOSTATE); + break; + } + mqstate(conn, MQTT_FIRST, MQTT_FIRST); + + if(mq->firstbyte == MQTT_MSG_DISCONNECT) { + infof(data, "Got DISCONNECT\n"); + *done = TRUE; + } + break; + case MQTT_CONNACK: + result = mqtt_verify_connack(conn); + if(result) + break; + + if(conn->data->state.httpreq == HTTPREQ_POST) { + result = mqtt_publish(conn); + if(!result) { + result = mqtt_disconnect(conn); + *done = TRUE; + } + mqtt->nextstate = MQTT_FIRST; + } + else { + result = mqtt_subscribe(conn); + if(!result) { + mqstate(conn, MQTT_FIRST, MQTT_SUBACK); + } + } + break; + + case MQTT_SUBACK: + case MQTT_PUBWAIT: + case MQTT_PUB_REMAIN: + result = mqtt_read_publish(conn, done); + break; + + default: + failf(conn->data, "State not handled yet"); + *done = TRUE; + break; + } + + if(result == CURLE_AGAIN) + result = CURLE_OK; + return result; +} + +#endif /* CURL_ENABLE_MQTT */ diff --git a/Utilities/cmcurl/lib/mqtt.h b/Utilities/cmcurl/lib/mqtt.h new file mode 100644 index 00000000000..37463d58ade --- /dev/null +++ b/Utilities/cmcurl/lib/mqtt.h @@ -0,0 +1,59 @@ +#ifndef HEADER_CURL_MQTT_H +#define HEADER_CURL_MQTT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2019 - 2020, Björn Stenberg, + * + * 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.haxx.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. + * + ***************************************************************************/ + +#ifdef CURL_ENABLE_MQTT +extern const struct Curl_handler Curl_handler_mqtt; +#endif + +enum mqttstate { + MQTT_FIRST, /* 0 */ + MQTT_REMAINING_LENGTH, /* 1 */ + MQTT_CONNACK, /* 2 */ + MQTT_SUBACK, /* 3 */ + MQTT_SUBACK_COMING, /* 4 - the SUBACK remainder */ + MQTT_PUBWAIT, /* 5 - wait for publish */ + MQTT_PUB_REMAIN, /* 6 - wait for the remainder of the publish */ + + MQTT_NOSTATE /* 7 - never used an actual state */ +}; + +struct mqtt_conn { + enum mqttstate state; + enum mqttstate nextstate; /* switch to this after remaining length is + done */ + unsigned int packetid; +}; + +/* protocol-specific transfer-related data */ +struct MQTT { + char *sendleftovers; + size_t nsend; /* size of sendleftovers */ + + /* when receiving */ + size_t npacket; /* byte counter */ + unsigned char firstbyte; + size_t remaining_length; +}; + +#endif /* HEADER_CURL_MQTT_H */ diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index c7c46eefc9d..249e3607b71 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -46,6 +46,8 @@ #include "connect.h" #include "http_proxy.h" #include "http2.h" +#include "socketpair.h" +#include "socks.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -71,15 +73,12 @@ static CURLMcode singlesocket(struct Curl_multi *multi, struct Curl_easy *data); -static int update_timer(struct Curl_multi *multi); - static CURLMcode add_next_timeout(struct curltime now, struct Curl_multi *multi, struct Curl_easy *d); static CURLMcode multi_timeout(struct Curl_multi *multi, long *timeout_ms); static void process_pending_handles(struct Curl_multi *multi); -static void detach_connnection(struct Curl_easy *data); #ifdef DEBUGBUILD static const char * const statename[]={ @@ -112,7 +111,7 @@ static void Curl_init_completed(struct Curl_easy *data) /* Important: reset the conn pointer so that we don't point to memory that could be freed anytime */ - detach_connnection(data); + Curl_detach_connnection(data); Curl_expire_clear(data); /* stop all timers */ } @@ -169,9 +168,11 @@ static void mstate(struct Curl_easy *data, CURLMstate state } #endif - if(state == CURLM_STATE_COMPLETED) + if(state == CURLM_STATE_COMPLETED) { /* changing to COMPLETED means there's one less easy handle 'alive' */ + DEBUGASSERT(data->multi->num_alive > 0); data->multi->num_alive--; + } /* if this state has an init-function, run it */ if(finit[state]) @@ -189,7 +190,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state */ struct Curl_sh_entry { - struct curl_llist list; /* list of easy handles using this socket */ + struct curl_hash transfers; /* hash of transfers using this socket */ unsigned int action; /* what combined action READ/WRITE this socket waits for */ void *socketp; /* settable by users with curl_multi_assign() */ @@ -206,12 +207,36 @@ struct Curl_sh_entry { static struct Curl_sh_entry *sh_getentry(struct curl_hash *sh, curl_socket_t s) { - if(s != CURL_SOCKET_BAD) + if(s != CURL_SOCKET_BAD) { /* only look for proper sockets */ return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t)); + } return NULL; } +#define TRHASH_SIZE 13 +static size_t trhash(void *key, size_t key_length, size_t slots_num) +{ + size_t keyval = (size_t)*(struct Curl_easy **)key; + (void) key_length; + + return (keyval % slots_num); +} + +static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) +{ + (void)k1_len; + (void)k2_len; + + return *(struct Curl_easy **)k1 == *(struct Curl_easy **)k2; +} + +static void trhash_dtor(void *nada) +{ + (void)nada; +} + + /* make sure this socket is present in the hash for this handle */ static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh, curl_socket_t s) @@ -219,19 +244,25 @@ static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh, struct Curl_sh_entry *there = sh_getentry(sh, s); struct Curl_sh_entry *check; - if(there) + if(there) { /* it is present, return fine */ return there; + } /* not present, add it */ check = calloc(1, sizeof(struct Curl_sh_entry)); if(!check) return NULL; /* major failure */ - Curl_llist_init(&check->list, NULL); + if(Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, + trhash_compare, trhash_dtor)) { + free(check); + return NULL; + } /* make/add new hash entry */ if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { + Curl_hash_destroy(&check->transfers); free(check); return NULL; /* major failure */ } @@ -241,8 +272,11 @@ static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh, /* delete the given socket + handle from the hash */ -static void sh_delentry(struct curl_hash *sh, curl_socket_t s) +static void sh_delentry(struct Curl_sh_entry *entry, + struct curl_hash *sh, curl_socket_t s) { + Curl_hash_destroy(&entry->transfers); + /* We remove the hash entry. This will end up in a call to sh_freeentry(). */ Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t)); @@ -311,17 +345,6 @@ static CURLMcode multi_addmsg(struct Curl_multi *multi, return CURLM_OK; } -/* - * multi_freeamsg() - * - * Callback used by the llist system when a single list entry is destroyed. - */ -static void multi_freeamsg(void *a, void *b) -{ - (void)a; - (void)b; -} - struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ int chashsize) /* connection hash */ { @@ -341,11 +364,30 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ if(Curl_conncache_init(&multi->conn_cache, chashsize)) goto error; - Curl_llist_init(&multi->msglist, multi_freeamsg); - Curl_llist_init(&multi->pending, multi_freeamsg); + Curl_llist_init(&multi->msglist, NULL); + Curl_llist_init(&multi->pending, NULL); + + multi->multiplexing = TRUE; /* -1 means it not set by user, use the default value */ multi->maxconnects = -1; + multi->max_concurrent_streams = 100; + multi->ipv6_works = Curl_ipv6works(NULL); + +#ifdef ENABLE_WAKEUP + if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) { + multi->wakeup_pair[0] = CURL_SOCKET_BAD; + multi->wakeup_pair[1] = CURL_SOCKET_BAD; + } + else if(curlx_nonblock(multi->wakeup_pair[0], TRUE) < 0 || + curlx_nonblock(multi->wakeup_pair[1], TRUE) < 0) { + sclose(multi->wakeup_pair[0]); + sclose(multi->wakeup_pair[1]); + multi->wakeup_pair[0] = CURL_SOCKET_BAD; + multi->wakeup_pair[1] = CURL_SOCKET_BAD; + } +#endif + return multi; error: @@ -453,18 +495,19 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, /* increase the alive-counter */ multi->num_alive++; - /* A somewhat crude work-around for a little glitch in update_timer() that - happens if the lastcall time is set to the same time when the handle is - removed as when the next handle is added, as then the check in - update_timer() that prevents calling the application multiple times with - the same timer info will not trigger and then the new handle's timeout - will not be notified to the app. + /* A somewhat crude work-around for a little glitch in Curl_update_timer() + that happens if the lastcall time is set to the same time when the handle + is removed as when the next handle is added, as then the check in + Curl_update_timer() that prevents calling the application multiple times + with the same timer info will not trigger and then the new handle's + timeout will not be notified to the app. The work-around is thus simply to clear the 'lastcall' variable to force - update_timer() to always trigger a callback to the app when a new easy - handle is added */ + Curl_update_timer() to always trigger a callback to the app when a new + easy handle is added */ memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); + CONNCACHE_LOCK(data); /* The closure handle only ever has default timeouts set. To improve the state somewhat we clone the timeouts from each added handle so that the closure handle always has the same timeouts as the most recently added @@ -474,8 +517,9 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->set.server_response_timeout; data->state.conn_cache->closure_handle->set.no_signal = data->set.no_signal; + CONNCACHE_UNLOCK(data); - update_timer(multi); + Curl_update_timer(multi); return CURLM_OK; } @@ -510,6 +554,8 @@ static CURLcode multi_done(struct Curl_easy *data, /* Stop if multi_done() has already been called */ return CURLE_OK; + conn->data = data; /* ensure the connection uses this transfer now */ + /* Stop the resolver and free its own resources (but not dns_entry yet). */ Curl_resolver_kill(conn); @@ -546,15 +592,20 @@ static CURLcode multi_done(struct Curl_easy *data, process_pending_handles(data->multi); /* connection / multiplex */ - detach_connnection(data); + CONNCACHE_LOCK(data); + Curl_detach_connnection(data); if(CONN_INUSE(conn)) { /* Stop if still used. */ + /* conn->data must not remain pointing to this transfer since it is going + away! Find another to own it! */ + conn->data = conn->easyq.head->ptr; + CONNCACHE_UNLOCK(data); DEBUGF(infof(data, "Connection still in use %zu, " "no more multi_done now!\n", conn->easyq.size)); return CURLE_OK; } - + conn->data = NULL; /* the connection now has no owner */ data->state.done = TRUE; /* called just now! */ if(conn->dns_entry) { @@ -567,7 +618,7 @@ static CURLcode multi_done(struct Curl_easy *data, /* if the transfer was completed in a paused state there can be buffered data left to free */ for(i = 0; i < data->state.tempcount; i++) { - free(data->state.tempwrite[i].buf); + Curl_dyn_free(&data->state.tempwrite[i].b); } data->state.tempcount = 0; @@ -597,7 +648,11 @@ static CURLcode multi_done(struct Curl_easy *data, #endif ) || conn->bits.close || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { - CURLcode res2 = Curl_disconnect(data, conn, premature); + CURLcode res2; + connclose(conn, "disconnecting"); + Curl_conncache_remove_conn(data, conn, FALSE); + CONNCACHE_UNLOCK(data); + res2 = Curl_disconnect(data, conn, premature); /* If we had an error already, make sure we return that one. But if we got a new error, return that. */ @@ -606,17 +661,21 @@ static CURLcode multi_done(struct Curl_easy *data, } else { char buffer[256]; + 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; /* create string before returning the connection */ msnprintf(buffer, sizeof(buffer), "Connection #%ld to host %s left intact", - conn->connection_id, - conn->bits.socksproxy ? conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname); - + conn->connection_id, host); /* the connection is no longer in use by this transfer */ - if(Curl_conncache_return_conn(conn)) { + CONNCACHE_UNLOCK(data); + if(Curl_conncache_return_conn(data, conn)) { /* remember the most recently used connection */ data->state.lastconnect = conn; infof(data, "%s\n", buffer); @@ -625,6 +684,7 @@ static CURLcode multi_done(struct Curl_easy *data, data->state.lastconnect = NULL; } + Curl_safefree(data->state.buffer); Curl_free_request_state(data); return result; } @@ -649,6 +709,10 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, if(!data->multi) return CURLM_OK; /* it is already removed so let's say it is fine! */ + /* Prevent users from trying to remove an easy handle from the wrong multi */ + if(data->multi != multi) + return CURLM_BAD_EASY_HANDLE; + if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; @@ -674,19 +738,14 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, easy_owns_conn = TRUE; } - /* The timer must be shut down before data->multi is set to NULL, - else the timenode will remain in the splay tree after - curl_easy_cleanup is called. */ - Curl_expire_clear(data); - if(data->conn) { /* we must call multi_done() here (if we still own the connection) so that we don't leave a half-baked one around */ if(easy_owns_conn) { - /* multi_done() clears the conn->data field to lose the association - between the easy handle and the connection + /* multi_done() clears the association between the easy handle and the + connection. Note that this ignores the return code simply because there's nothing really useful to do with it anyway! */ @@ -694,6 +753,11 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, } } + /* The timer must be shut down before data->multi is set to NULL, else the + timenode will remain in the splay tree after curl_easy_cleanup is + called. Do it after multi_done() in case that sets another time! */ + Curl_expire_clear(data); + if(data->connect_queue.ptr) /* the handle was in the pending list waiting for an available connection, so go ahead and remove it */ @@ -723,10 +787,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, vanish with this handle */ /* Remove the association between the connection and the handle */ - if(data->conn) { - data->conn->data = NULL; - detach_connnection(data); - } + Curl_detach_connnection(data); #ifdef USE_LIBPSL /* Remove the PSL association. */ @@ -765,7 +826,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, We do not touch the easy handle here! */ multi->num_easy--; /* one less to care about now */ - update_timer(multi); + Curl_update_timer(multi); return CURLM_OK; } @@ -775,9 +836,13 @@ bool Curl_multiplex_wanted(const struct Curl_multi *multi) return (multi && (multi->multiplexing)); } -/* This is the only function that should clear data->conn. This will - occasionally be called with the pointer already cleared. */ -static void detach_connnection(struct Curl_easy *data) +/* + * Curl_detach_connnection() removes the given transfer from the connection. + * + * This is the only function that should clear data->conn. This will + * occasionally be called with the data->conn pointer already cleared. + */ +void Curl_detach_connnection(struct Curl_easy *data) { struct connectdata *conn = data->conn; if(conn) @@ -785,7 +850,11 @@ static void detach_connnection(struct Curl_easy *data) data->conn = NULL; } -/* This is the only function that should assign data->conn */ +/* + * Curl_attach_connnection() attaches this transfer to this connection. + * + * This is the only function that should assign data->conn + */ void Curl_attach_connnection(struct Curl_easy *data, struct connectdata *conn) { @@ -797,25 +866,32 @@ void Curl_attach_connnection(struct Curl_easy *data, } static int waitconnect_getsock(struct connectdata *conn, - curl_socket_t *sock, - int numsocks) + curl_socket_t *sock) { int i; int s = 0; int rc = 0; - if(!numsocks) - return GETSOCK_BLANK; - #ifdef USE_SSL +#ifndef CURL_DISABLE_PROXY if(CONNECT_FIRSTSOCKET_PROXY_SSL()) - return Curl_ssl_getsock(conn, sock, numsocks); + return Curl_ssl_getsock(conn, sock); #endif +#endif + + if(SOCKS_STATE(conn->cnnct.state)) + return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET); for(i = 0; i<2; i++) { if(conn->tempsock[i] != CURL_SOCKET_BAD) { sock[s] = conn->tempsock[i]; - rc |= GETSOCK_WRITESOCK(s++); + rc |= GETSOCK_WRITESOCK(s); +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) + /* when connecting QUIC, we want to read the socket too */ + rc |= GETSOCK_READSOCK(s); +#endif + s++; } } @@ -823,12 +899,8 @@ static int waitconnect_getsock(struct connectdata *conn, } static int waitproxyconnect_getsock(struct connectdata *conn, - curl_socket_t *sock, - int numsocks) + curl_socket_t *sock) { - if(!numsocks) - return GETSOCK_BLANK; - sock[0] = conn->sock[FIRSTSOCKET]; /* when we've sent a CONNECT to a proxy, we should rather wait for the @@ -840,19 +912,37 @@ static int waitproxyconnect_getsock(struct connectdata *conn, } static int domore_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { if(conn && conn->handler->domore_getsock) - return conn->handler->domore_getsock(conn, socks, numsocks); + return conn->handler->domore_getsock(conn, socks); return GETSOCK_BLANK; } -/* returns bitmapped flags for this handle and its sockets */ +static int doing_getsock(struct connectdata *conn, + curl_socket_t *socks) +{ + if(conn && conn->handler->doing_getsock) + return conn->handler->doing_getsock(conn, socks); + return GETSOCK_BLANK; +} + +static int protocol_getsock(struct connectdata *conn, + curl_socket_t *socks) +{ + if(conn->handler->proto_getsock) + return conn->handler->proto_getsock(conn, socks); + /* Backup getsock logic. Since there is a live socket in use, we must wait + for it or it will be removed from watching when the multi_socket API is + used. */ + socks[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); +} + +/* returns bitmapped flags for this handle and its sockets. The 'socks[]' + array contains MAX_SOCKSPEREASYHANDLE entries. */ static int multi_getsock(struct Curl_easy *data, - curl_socket_t *socks, /* points to numsocks number - of sockets */ - int numsocks) + curl_socket_t *socks) { /* The no connection case can happen when this is called from curl_multi_remove_handle() => singlesocket() => multi_getsock(). @@ -884,30 +974,30 @@ static int multi_getsock(struct Curl_easy *data, return 0; case CURLM_STATE_WAITRESOLVE: - return Curl_resolv_getsock(data->conn, socks, numsocks); + return Curl_resolv_getsock(data->conn, socks); case CURLM_STATE_PROTOCONNECT: case CURLM_STATE_SENDPROTOCONNECT: - return Curl_protocol_getsock(data->conn, socks, numsocks); + return protocol_getsock(data->conn, socks); case CURLM_STATE_DO: case CURLM_STATE_DOING: - return Curl_doing_getsock(data->conn, socks, numsocks); + return doing_getsock(data->conn, socks); case CURLM_STATE_WAITPROXYCONNECT: - return waitproxyconnect_getsock(data->conn, socks, numsocks); + return waitproxyconnect_getsock(data->conn, socks); case CURLM_STATE_WAITCONNECT: - return waitconnect_getsock(data->conn, socks, numsocks); + return waitconnect_getsock(data->conn, socks); case CURLM_STATE_DO_MORE: - return domore_getsock(data->conn, socks, numsocks); + return domore_getsock(data->conn, socks); case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch to waiting for the same as the *PERFORM states */ case CURLM_STATE_PERFORM: - return Curl_single_getsock(data->conn, socks, numsocks); + return Curl_single_getsock(data->conn, socks); } } @@ -933,7 +1023,7 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, data = multi->easyp; while(data) { - int bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE); + int bitmap = multi_getsock(data, sockbunch); for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { curl_socket_t s = CURL_SOCKET_BAD; @@ -963,12 +1053,13 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, #define NUM_POLLS_ON_STACK 10 -CURLMcode Curl_multi_wait(struct Curl_multi *multi, - struct curl_waitfd extra_fds[], - unsigned int extra_nfds, - int timeout_ms, - int *ret, - bool *gotsocket) /* if any socket was checked */ +static CURLMcode Curl_multi_wait(struct Curl_multi *multi, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret, + bool extrawait, /* when no socket, wait */ + bool use_wakeup) { struct Curl_easy *data; curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; @@ -982,19 +1073,19 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi, struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct pollfd *ufds = &a_few_on_stack[0]; - if(gotsocket) - *gotsocket = FALSE; - if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + if(timeout_ms < 0) + return CURLM_BAD_FUNCTION_ARGUMENT; + /* Count up how many fds we have from the multi handle */ data = multi->easyp; while(data) { - bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE); + bitmap = multi_getsock(data, sockbunch); for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { curl_socket_t s = CURL_SOCKET_BAD; @@ -1025,6 +1116,12 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi, curlfds = nfds; /* number of internal file descriptors */ nfds += extra_nfds; /* add the externally provided ones */ +#ifdef ENABLE_WAKEUP + if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { + ++nfds; + } +#endif + if(nfds > NUM_POLLS_ON_STACK) { /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes big, so at 2^29 sockets this value might wrap. When a process gets @@ -1044,7 +1141,7 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi, /* Add the curl handles to our pollfds first */ data = multi->easyp; while(data) { - bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE); + bitmap = multi_getsock(data, sockbunch); for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { curl_socket_t s = CURL_SOCKET_BAD; @@ -1083,6 +1180,14 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi, ++nfds; } +#ifdef ENABLE_WAKEUP + if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { + ufds[nfds].fd = multi->wakeup_pair[0]; + ufds[nfds].events = POLLIN; + ++nfds; + } +#endif + if(nfds) { int pollrc; /* wait... */ @@ -1106,6 +1211,31 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi, extra_fds[i].revents = mask; } + +#ifdef ENABLE_WAKEUP + if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { + if(ufds[curlfds + extra_nfds].revents & POLLIN) { + char buf[64]; + ssize_t nread; + while(1) { + /* the reading socket is non-blocking, try to read + data from it until it receives an error (except EINTR). + In normal cases it will get EAGAIN or EWOULDBLOCK + when there is no more data, breaking the loop. */ + nread = sread(multi->wakeup_pair[0], buf, sizeof(buf)); + if(nread <= 0) { +#ifndef USE_WINSOCK + if(nread < 0 && EINTR == SOCKERRNO) + continue; +#endif + break; + } + } + /* do not count the wakeup socket into the returned value */ + retcode--; + } + } +#endif } } @@ -1113,9 +1243,23 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi, free(ufds); if(ret) *ret = retcode; - if(gotsocket && (extra_fds || curlfds)) + if(!extrawait || nfds) /* if any socket was checked */ - *gotsocket = TRUE; + ; + else { + long sleep_ms = 0; + + /* Avoid busy-looping when there's nothing particular to wait for */ + if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) { + if(sleep_ms > timeout_ms) + sleep_ms = timeout_ms; + /* when there are no easy handles in the multi, this holds a -1 + timeout */ + else if((sleep_ms < 0) && extrawait) + sleep_ms = timeout_ms; + Curl_wait_ms(sleep_ms); + } + } return CURLM_OK; } @@ -1126,7 +1270,65 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi, int timeout_ms, int *ret) { - return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, NULL); + return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE, + FALSE); +} + +CURLMcode curl_multi_poll(struct Curl_multi *multi, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret) +{ + return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE, + TRUE); +} + +CURLMcode curl_multi_wakeup(struct Curl_multi *multi) +{ + /* this function is usually called from another thread, + it has to be careful only to access parts of the + Curl_multi struct that are constant */ + + /* GOOD_MULTI_HANDLE can be safely called */ + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + +#ifdef ENABLE_WAKEUP + /* 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) { + char buf[1]; + buf[0] = 1; + while(1) { + /* swrite() is not thread-safe in general, because concurrent calls + can have their messages interleaved, but in this case the content + of the messages does not matter, which makes it ok to call. + + The write socket is set to non-blocking, this way this function + cannot block, making it safe to call even from the same thread + that will call Curl_multi_wait(). If swrite() returns that it + would block, it's considered successful because it means that + previous calls to this function will wake up the poll(). */ + if(swrite(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) { + int err = SOCKERRNO; + int return_success; +#ifdef USE_WINSOCK + return_success = WSAEWOULDBLOCK == err; +#else + if(EINTR == err) + continue; + return_success = EWOULDBLOCK == err || EAGAIN == err; +#endif + if(!return_success) + return CURLM_WAKEUP_FAILURE; + } + return CURLM_OK; + } + } +#endif + return CURLM_WAKEUP_FAILURE; } /* @@ -1189,6 +1391,7 @@ static CURLcode multi_do(struct Curl_easy *data, bool *done) DEBUGASSERT(conn); DEBUGASSERT(conn->handler); + DEBUGASSERT(conn->data == data); if(conn->handler->do_it) { /* generic protocol-specific function pointer set in curl_connect() */ @@ -1226,6 +1429,124 @@ static CURLcode multi_do_more(struct connectdata *conn, int *complete) return result; } +/* + * We are doing protocol-specific connecting and this is being called over and + * over from the multi interface until the connection phase is done on + * protocol layer. + */ + +static CURLcode protocol_connecting(struct connectdata *conn, + bool *done) +{ + CURLcode result = CURLE_OK; + + if(conn && conn->handler->connecting) { + *done = FALSE; + result = conn->handler->connecting(conn, done); + } + else + *done = TRUE; + + return result; +} + +/* + * We are DOING this is being called over and over from the multi interface + * until the DOING phase is done on protocol layer. + */ + +static CURLcode protocol_doing(struct connectdata *conn, bool *done) +{ + CURLcode result = CURLE_OK; + + if(conn && conn->handler->doing) { + *done = FALSE; + result = conn->handler->doing(conn, done); + } + else + *done = TRUE; + + return result; +} + +/* + * We have discovered that the TCP connection has been successful, we can now + * proceed with some action. + * + */ +static CURLcode protocol_connect(struct connectdata *conn, + bool *protocol_done) +{ + CURLcode result = CURLE_OK; + + DEBUGASSERT(conn); + DEBUGASSERT(protocol_done); + + *protocol_done = FALSE; + + if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) { + /* We already are connected, get back. This may happen when the connect + worked fine in the first call, like when we connect to a local server + or proxy. Note that we don't know if the protocol is actually done. + + Unless this protocol doesn't have any protocol-connect callback, as + then we know we're done. */ + if(!conn->handler->connecting) + *protocol_done = TRUE; + + return CURLE_OK; + } + + if(!conn->bits.protoconnstart) { +#ifndef CURL_DISABLE_PROXY + result = Curl_proxy_connect(conn, FIRSTSOCKET); + if(result) + return result; + + if(CONNECT_FIRSTSOCKET_PROXY_SSL()) + /* wait for HTTPS proxy SSL initialization to complete */ + return CURLE_OK; + + if(conn->bits.tunnel_proxy && conn->bits.httpproxy && + Curl_connect_ongoing(conn)) + /* when using an HTTP tunnel proxy, await complete tunnel establishment + before proceeding further. Return CURLE_OK so we'll be called again */ + return CURLE_OK; +#endif + if(conn->handler->connect_it) { + /* is there a protocol-specific connect() procedure? */ + + /* Call the protocol-specific connect function */ + result = conn->handler->connect_it(conn, protocol_done); + } + else + *protocol_done = TRUE; + + /* it has started, possibly even completed but that knowledge isn't stored + in this bit! */ + if(!result) + conn->bits.protoconnstart = TRUE; + } + + return result; /* pass back status */ +} + +/* + * Curl_preconnect() is called immediately before a connect starts. When a + * redirect is followed, this is then called multiple times during a single + * transfer. + */ +CURLcode Curl_preconnect(struct Curl_easy *data) +{ + if(!data->state.buffer) { + data->state.buffer = malloc(data->set.buffer_size + 1); + if(!data->state.buffer) + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; +} + + static CURLMcode multi_runsingle(struct Curl_multi *multi, struct curltime now, struct Curl_easy *data) @@ -1233,7 +1554,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, struct Curl_message *msg = NULL; bool connected; bool async; - bool protocol_connect = FALSE; + bool protocol_connected = FALSE; bool dophase_done = FALSE; bool done = FALSE; CURLMcode rc; @@ -1252,16 +1573,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, bool stream_error = FALSE; rc = CURLM_OK; - if(!data->conn && - data->mstate > CURLM_STATE_CONNECT && - data->mstate < CURLM_STATE_DONE) { - /* In all these states, the code will blindly access 'data->conn' - so this is precaution that it isn't NULL. And it silences static - analyzers. */ - failf(data, "In state %d with no conn, bail out!\n", data->mstate); - return CURLM_INTERNAL_ERROR; - } - if(multi_ischanged(multi, TRUE)) { DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n")); process_pending_handles(multi); /* multiplexed */ @@ -1342,6 +1653,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_CONNECT: /* Connect. We want to get a connection identifier filled in. */ + /* init this transfer. */ + result = Curl_preconnect(data); + if(result) + break; + Curl_pgrsTime(data, TIMER_STARTSINGLE); if(data->set.timeout) Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); @@ -1349,7 +1665,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(data->set.connecttimeout) Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); - result = Curl_connect(data, &async, &protocol_connect); + result = Curl_connect(data, &async, &protocol_connected); if(CURLE_NO_CONNECTION_AVAILABLE == result) { /* There was no connection available. We will go to the pending state and wait for an available connection. */ @@ -1377,7 +1693,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, WAITDO or DO! */ rc = CURLM_CALL_MULTI_PERFORM; - if(protocol_connect) + if(protocol_connected) multistate(data, CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP @@ -1399,9 +1715,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, const char *hostname; DEBUGASSERT(conn); +#ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy) hostname = conn->http_proxy.host.name; - else if(conn->bits.conn_to_host) + else +#endif + if(conn->bits.conn_to_host) hostname = conn->conn_to_host.name; else hostname = conn->host.name; @@ -1432,7 +1751,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ - result = Curl_once_resolved(data->conn, &protocol_connect); + result = Curl_once_resolved(data->conn, &protocol_connected); if(result) /* if Curl_once_resolved() returns failure, the connection struct @@ -1441,7 +1760,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* call again please so that we get the next socket setup */ rc = CURLM_CALL_MULTI_PERFORM; - if(protocol_connect) + if(protocol_connected) multistate(data, CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP @@ -1466,8 +1785,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITPROXYCONNECT: /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ DEBUGASSERT(data->conn); - result = Curl_http_connect(data->conn, &protocol_connect); - + result = Curl_http_connect(data->conn, &protocol_connected); +#ifndef CURL_DISABLE_PROXY if(data->conn->bits.proxy_connect_closed) { rc = CURLM_CALL_MULTI_PERFORM; /* connect back to proxy again */ @@ -1475,15 +1794,20 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multi_done(data, CURLE_OK, FALSE); multistate(data, CURLM_STATE_CONNECT); } - else if(!result) { - if((data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || - data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && - Curl_connect_complete(data->conn)) { - rc = CURLM_CALL_MULTI_PERFORM; - /* initiate protocol connect phase */ - multistate(data, CURLM_STATE_SENDPROTOCONNECT); + else +#endif + if(!result) { + if( +#ifndef CURL_DISABLE_PROXY + (data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || + data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && +#endif + Curl_connect_complete(data->conn)) { + rc = CURLM_CALL_MULTI_PERFORM; + /* initiate protocol connect phase */ + multistate(data, CURLM_STATE_SENDPROTOCONNECT); + } } - } else if(result) stream_error = TRUE; break; @@ -1495,17 +1819,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = Curl_is_connected(data->conn, FIRSTSOCKET, &connected); if(connected && !result) { #ifndef CURL_DISABLE_HTTP - if((data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && - !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || - Curl_connect_ongoing(data->conn)) { + if( +#ifndef CURL_DISABLE_PROXY + (data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && + !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || +#endif + Curl_connect_ongoing(data->conn)) { multistate(data, CURLM_STATE_WAITPROXYCONNECT); break; } #endif rc = CURLM_CALL_MULTI_PERFORM; - multistate(data, data->conn->bits.tunnel_proxy? +#ifndef CURL_DISABLE_PROXY + multistate(data, + data->conn->bits.tunnel_proxy? CURLM_STATE_WAITPROXYCONNECT: CURLM_STATE_SENDPROTOCONNECT); +#else + multistate(data, CURLM_STATE_SENDPROTOCONNECT); +#endif } else if(result) { /* failure detected */ @@ -1517,8 +1849,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_SENDPROTOCONNECT: - result = Curl_protocol_connect(data->conn, &protocol_connect); - if(!result && !protocol_connect) + result = protocol_connect(data->conn, &protocol_connected); + if(!result && !protocol_connected) /* switch to waiting state */ multistate(data, CURLM_STATE_PROTOCONNECT); else if(!result) { @@ -1536,8 +1868,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_PROTOCONNECT: /* protocol-specific connect phase */ - result = Curl_protocol_connecting(data->conn, &protocol_connect); - if(!result && protocol_connect) { + result = protocol_connecting(data->conn, &protocol_connected); + if(!result && protocol_connected) { /* after the connect has completed, go WAITDO or DO */ multistate(data, CURLM_STATE_DO); rc = CURLM_CALL_MULTI_PERFORM; @@ -1659,8 +1991,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DOING: /* we continue DOING until the DO phase is complete */ DEBUGASSERT(data->conn); - result = Curl_protocol_doing(data->conn, - &dophase_done); + result = protocol_doing(data->conn, &dophase_done); if(!result) { if(dophase_done) { /* after DO, go DO_DONE or DO_MORE */ @@ -1772,7 +2103,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, char *newurl = NULL; bool retry = FALSE; bool comeback = FALSE; - + DEBUGASSERT(data->state.buffer); /* check if over send speed */ send_timeout_ms = 0; if(data->set.max_send_speed > 0) @@ -1911,8 +2242,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } } - else if(comeback) - rc = CURLM_CALL_MULTI_PERFORM; + else if(comeback) { + /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer + won't get stuck on this transfer at the expense of other concurrent + transfers */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + rc = CURLM_OK; + } break; } @@ -1940,8 +2276,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * access free'd data, if the connection is free'd and the handle * removed before we perform the processing in CURLM_STATE_COMPLETED */ - if(data->conn) - detach_connnection(data); + Curl_detach_connnection(data); } #ifndef CURL_DISABLE_FTP @@ -1988,13 +2323,18 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(stream_error) { /* Don't attempt to send data over a connection that timed out */ bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; - /* disconnect properly */ - Curl_disconnect(data, data->conn, dead_connection); + struct connectdata *conn = data->conn; /* This is where we make sure that the conn pointer is reset. We don't have to do this in every case block above where a failure is detected */ - detach_connnection(data); + Curl_detach_connnection(data); + + /* remove connection from cache */ + Curl_conncache_remove_conn(data, conn, TRUE); + + /* disconnect properly */ + Curl_disconnect(data, conn, dead_connection); } } else if(data->mstate == CURLM_STATE_CONNECT) { @@ -2093,7 +2433,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) *running_handles = multi->num_alive; if(CURLM_OK >= returncode) - update_timer(multi); + Curl_update_timer(multi); return returncode; } @@ -2145,6 +2485,11 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) Curl_hash_destroy(&multi->hostcache); Curl_psl_destroy(&multi->psl); + +#ifdef ENABLE_WAKEUP + sclose(multi->wakeup_pair[0]); + sclose(multi->wakeup_pair[1]); +#endif free(multi); return CURLM_OK; @@ -2210,7 +2555,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* Fill in the 'current' struct with the state as it is now: what sockets to supervise and for what actions */ - curraction = multi_getsock(data, socks, MAX_SOCKSPEREASYHANDLE); + curraction = multi_getsock(data, socks); /* We have 0 .. N sockets already and we get to know about the 0 .. M sockets we should have from now on. Detect the differences, remove no @@ -2238,14 +2583,14 @@ static CURLMcode singlesocket(struct Curl_multi *multi, actions[i] = action; if(entry) { /* check if new for this transfer */ - for(i = 0; i< data->numsocks; i++) { - if(s == data->sockets[i]) { - prevaction = data->actions[i]; + int j; + for(j = 0; j< data->numsocks; j++) { + if(s == data->sockets[j]) { + prevaction = data->actions[j]; sincebefore = TRUE; break; } } - } else { /* this is a socket we didn't have before, add it to the hash! */ @@ -2273,29 +2618,22 @@ static CURLMcode singlesocket(struct Curl_multi *multi, if(action & CURL_POLL_OUT) entry->writers++; - /* add 'data' to the list of handles using this socket! */ - Curl_llist_insert_next(&entry->list, entry->list.tail, - data, &data->sh_queue); + /* add 'data' to the transfer hash on this socket! */ + if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */ + sizeof(struct Curl_easy *), data)) + return CURLM_OUT_OF_MEMORY; } comboaction = (entry->writers? CURL_POLL_OUT : 0) | (entry->readers ? CURL_POLL_IN : 0); -#if 0 - infof(data, "--- Comboaction: %u readers %u writers\n", - entry->readers, entry->writers); -#endif - /* check if it has the same action set */ - if(entry->action == comboaction) + /* socket existed before and has the same action set as before */ + if(sincebefore && (entry->action == comboaction)) /* same, continue */ continue; - /* we know (entry != NULL) at this point, see the logic above */ if(multi->socket_cb) - multi->socket_cb(data, - s, - comboaction, - multi->socket_userp, + multi->socket_cb(data, s, comboaction, multi->socket_userp, entry->socketp); entry->action = comboaction; /* store the current action state */ @@ -2335,11 +2673,14 @@ static CURLMcode singlesocket(struct Curl_multi *multi, multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->socketp); - sh_delentry(&multi->sockhash, s); + sh_delentry(entry, &multi->sockhash, s); } else { - /* remove this transfer as a user of this socket */ - Curl_llist_remove(&entry->list, &data->sh_queue, NULL); + /* still users, but remove this handle as a user of this socket */ + if(Curl_hash_delete(&entry->transfers, (char *)&data, + sizeof(struct Curl_easy *))) { + DEBUGASSERT(NULL); + } } } } /* for loop over numsocks */ @@ -2383,7 +2724,7 @@ void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s) entry->socketp); /* now remove it from the socket hash */ - sh_delentry(&multi->sockhash, s); + sh_delentry(entry, &multi->sockhash, s); } } } @@ -2474,7 +2815,6 @@ static CURLMcode multi_socket(struct Curl_multi *multi, return result; } if(s != CURL_SOCKET_TIMEOUT) { - struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); if(!entry) @@ -2485,37 +2825,22 @@ static CURLMcode multi_socket(struct Curl_multi *multi, and just move on. */ ; else { - struct curl_llist *list = &entry->list; - struct curl_llist_element *e; - SIGPIPE_VARIABLE(pipe_st); + struct curl_hash_iterator iter; + struct curl_hash_element *he; /* the socket can be shared by many transfers, iterate */ - for(e = list->head; e; e = e->next) { - data = (struct Curl_easy *)e->ptr; - - if(data->magic != CURLEASY_MAGIC_NUMBER) - /* bad bad bad bad bad bad bad */ - return CURLM_INTERNAL_ERROR; + Curl_hash_start_iterate(&entry->transfers, &iter); + for(he = Curl_hash_next_element(&iter); he; + he = Curl_hash_next_element(&iter)) { + data = (struct Curl_easy *)he->ptr; + DEBUGASSERT(data); + DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER); if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) /* set socket event bitmask if they're not locked */ data->conn->cselect_bits = ev_bitmask; - sigpipe_ignore(data, &pipe_st); - result = multi_runsingle(multi, now, data); - sigpipe_restore(&pipe_st); - - if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) - /* clear the bitmask only if not locked */ - data->conn->cselect_bits = 0; - - if(CURLM_OK >= result) { - /* get the socket(s) and check if the state has been changed since - last */ - result = singlesocket(multi, data); - if(result) - return result; - } + Curl_expire(data, 0, EXPIRE_RUN_NOW); } /* Now we fall-through and do the timer-based stuff, since we don't want @@ -2530,9 +2855,10 @@ static CURLMcode multi_socket(struct Curl_multi *multi, } else { /* Asked to run due to time-out. Clear the 'lastcall' variable to force - update_timer() to trigger a callback to the app again even if the same - timeout is still the one to run after this call. That handles the case - when the application asks libcurl to run the timeout prematurely. */ + Curl_update_timer() to trigger a callback to the app again even if the + same timeout is still the one to run after this call. That handles the + case when the application asks libcurl to run the timeout + prematurely. */ memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); } @@ -2631,6 +2957,16 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, break; case CURLMOPT_PIPELINING_SERVER_BL: break; + case CURLMOPT_MAX_CONCURRENT_STREAMS: + { + long streams = va_arg(param, long); + if(streams < 1) + streams = 100; + multi->max_concurrent_streams = + (streams > (long)INITIAL_MAX_CONCURRENT_STREAMS)? + INITIAL_MAX_CONCURRENT_STREAMS : (unsigned int)streams; + } + break; default: res = CURLM_UNKNOWN_OPTION; break; @@ -2650,7 +2986,7 @@ CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, return CURLM_RECURSIVE_API_CALL; result = multi_socket(multi, FALSE, s, 0, running_handles); if(CURLM_OK >= result) - update_timer(multi); + Curl_update_timer(multi); return result; } @@ -2662,7 +2998,7 @@ CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, return CURLM_RECURSIVE_API_CALL; result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles); if(CURLM_OK >= result) - update_timer(multi); + Curl_update_timer(multi); return result; } @@ -2674,7 +3010,7 @@ CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) return CURLM_RECURSIVE_API_CALL; result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); if(CURLM_OK >= result) - update_timer(multi); + Curl_update_timer(multi); return result; } @@ -2734,14 +3070,14 @@ CURLMcode curl_multi_timeout(struct Curl_multi *multi, * Tell the application it should update its timers, if it subscribes to the * update timer callback. */ -static int update_timer(struct Curl_multi *multi) +void Curl_update_timer(struct Curl_multi *multi) { long timeout_ms; if(!multi->timer_cb) - return 0; + return; if(multi_timeout(multi, &timeout_ms)) { - return -1; + return; } if(timeout_ms < 0) { static const struct curltime none = {0, 0}; @@ -2749,9 +3085,10 @@ static int update_timer(struct Curl_multi *multi) multi->timer_lastcall = none; /* there's no timeout now but there was one previously, tell the app to disable it */ - return multi->timer_cb(multi, -1, multi->timer_userp); + multi->timer_cb(multi, -1, multi->timer_userp); + return; } - return 0; + return; } /* When multi_timeout() is done, multi->timetree points to the node with the @@ -2759,11 +3096,11 @@ static int update_timer(struct Curl_multi *multi) * if this is the same (fixed) time as we got in a previous call and then * avoid calling the callback again. */ if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0) - return 0; + return; multi->timer_lastcall = multi->timetree->key; - return multi->timer_cb(multi, timeout_ms, multi->timer_userp); + multi->timer_cb(multi, timeout_ms, multi->timer_userp); } /* @@ -2840,7 +3177,7 @@ multi_addtimeout(struct Curl_easy *data, * * Expire replaces a former timeout using the same id if already set. */ -void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id) +void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) { struct Curl_multi *multi = data->multi; struct curltime *nowp = &data->state.expiretime; @@ -2854,7 +3191,7 @@ void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id) DEBUGASSERT(id < EXPIRE_LAST); set = Curl_now(); - set.tv_sec += milli/1000; + set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bit conversion */ set.tv_usec += (unsigned int)(milli%1000)*1000; if(set.tv_usec >= 1000000) { @@ -3068,3 +3405,9 @@ void Curl_multi_dump(struct Curl_multi *multi) } } #endif + +unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) +{ + DEBUGASSERT(multi); + return multi->max_concurrent_streams; +} diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index 279379ae0f3..91eca16c4a8 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,6 +24,7 @@ #include "conncache.h" #include "psl.h" +#include "socketpair.h" struct Curl_message { struct curl_llist_element list; @@ -66,6 +67,14 @@ typedef enum { #define CURLPIPE_ANY (CURLPIPE_MULTIPLEX) +#if defined(USE_SOCKETPAIR) && !defined(USE_BLOCKING_SOCKETS) +#define ENABLE_WAKEUP +#endif + + +/* value for MAXIMUM CONCURRENT STREAMS upper limit */ +#define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) + /* This is the struct known as CURLM on the outside */ struct Curl_multi { /* First a simple identifier to easier detect if a user mix up @@ -110,11 +119,6 @@ struct Curl_multi { same actual socket) */ struct curl_hash sockhash; - /* multiplexing wanted */ - bool multiplexing; - - bool recheckstate; /* see Curl_multi_connchanged */ - /* Shared connection cache (bundles)*/ struct conncache conn_cache; @@ -132,7 +136,17 @@ struct Curl_multi { void *timer_userp; struct curltime timer_lastcall; /* the fixed time for the timeout for the previous callback */ + unsigned int max_concurrent_streams; + +#ifdef ENABLE_WAKEUP + curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup + 0 is used for read, 1 is used for write */ +#endif + /* multiplexing wanted */ + bool multiplexing; + bool recheckstate; /* see Curl_multi_connchanged */ bool in_callback; /* true while executing a callback */ + bool ipv6_works; }; #endif /* HEADER_CURL_MULTIHANDLE_H */ diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h index e8a5e7062de..7d574df9241 100644 --- a/Utilities/cmcurl/lib/multiif.h +++ b/Utilities/cmcurl/lib/multiif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,15 +27,17 @@ */ void Curl_updatesocket(struct Curl_easy *data); -void Curl_expire(struct Curl_easy *data, time_t milli, expire_id); +void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id); void Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); -void Curl_detach_connnection(struct Curl_easy *data); +void Curl_update_timer(struct Curl_multi *multi); void Curl_attach_connnection(struct Curl_easy *data, struct connectdata *conn); +void Curl_detach_connnection(struct Curl_easy *data); bool Curl_multiplex_wanted(const struct Curl_multi *multi); void Curl_set_in_callback(struct Curl_easy *data, bool value); bool Curl_is_in_callback(struct Curl_easy *easy); +CURLcode Curl_preconnect(struct Curl_easy *data); /* Internal version of curl_multi_init() accepts size parameters for the socket and connection hashes */ @@ -89,11 +91,8 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, struct Curl_easy *data, struct connectdata *conn); -CURLMcode Curl_multi_wait(struct Curl_multi *multi, - struct curl_waitfd extra_fds[], - unsigned int extra_nfds, - int timeout_ms, - int *ret, - bool *gotsocket); /* if any socket was checked */ + +/* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */ +unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi); #endif /* HEADER_CURL_MULTIIF_H */ diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c index 1bd998f9c5f..1c9da31993c 100644 --- a/Utilities/cmcurl/lib/netrc.c +++ b/Utilities/cmcurl/lib/netrc.c @@ -45,27 +45,27 @@ enum host_lookup_state { HOSTVALID /* this is "our" machine! */ }; +#define NETRC_FILE_MISSING 1 +#define NETRC_FAILED -1 +#define NETRC_SUCCESS 0 + /* - * @unittest: 1304 - * - * *loginp and *passwordp MUST be allocated if they aren't NULL when passed - * in. + * Returns zero on success. */ -int Curl_parsenetrc(const char *host, - char **loginp, - char **passwordp, - bool *login_changed, - bool *password_changed, - char *netrcfile) +static int parsenetrc(const char *host, + char **loginp, + char **passwordp, + bool *login_changed, + bool *password_changed, + char *netrcfile) { FILE *file; - int retcode = 1; + int retcode = NETRC_FILE_MISSING; char *login = *loginp; char *password = *passwordp; bool specific_login = (login && *login != 0); bool login_alloc = FALSE; bool password_alloc = FALSE; - bool netrc_alloc = FALSE; enum host_lookup_state state = NOTHING; char state_login = 0; /* Found a login keyword */ @@ -73,51 +73,9 @@ int Curl_parsenetrc(const char *host, int state_our_login = FALSE; /* With specific_login, found *our* login name */ -#define NETRC DOT_CHAR "netrc" - - if(!netrcfile) { - bool home_alloc = FALSE; - char *home = curl_getenv("HOME"); /* portable environment reader */ - if(home) { - home_alloc = TRUE; -#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) - } - else { - struct passwd pw, *pw_res; - char pwbuf[1024]; - if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) - && pw_res) { - home = strdup(pw.pw_dir); - if(!home) - return CURLE_OUT_OF_MEMORY; - home_alloc = TRUE; - } -#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID) - } - else { - struct passwd *pw; - pw = getpwuid(geteuid()); - if(pw) { - home = pw->pw_dir; - } -#endif - } - - if(!home) - return retcode; /* no home directory found (or possibly out of memory) */ - - netrcfile = curl_maprintf("%s%s%s", home, DIR_CHAR, NETRC); - if(home_alloc) - free(home); - if(!netrcfile) { - return -1; - } - netrc_alloc = TRUE; - } + DEBUGASSERT(netrcfile); file = fopen(netrcfile, FOPEN_READTEXT); - if(netrc_alloc) - free(netrcfile); if(file) { char *tok; char *tok_buf; @@ -130,7 +88,7 @@ int Curl_parsenetrc(const char *host, if(tok && *tok == '#') /* treat an initial hash as a comment line */ continue; - while(!done && tok) { + while(tok) { if((login && *login) && (password && *password)) { done = TRUE; @@ -148,14 +106,14 @@ int Curl_parsenetrc(const char *host, } else if(strcasecompare("default", tok)) { state = HOSTVALID; - retcode = 0; /* we did find our host */ + retcode = NETRC_SUCCESS; /* we did find our host */ } break; case HOSTFOUND: if(strcasecompare(host, tok)) { /* and yes, this is our host! */ state = HOSTVALID; - retcode = 0; /* we did find our host */ + retcode = NETRC_SUCCESS; /* we did find our host */ } else /* not our host */ @@ -174,7 +132,7 @@ int Curl_parsenetrc(const char *host, } login = strdup(tok); if(!login) { - retcode = -1; /* allocation failed */ + retcode = NETRC_FAILED; /* allocation failed */ goto out; } login_alloc = TRUE; @@ -190,7 +148,7 @@ int Curl_parsenetrc(const char *host, } password = strdup(tok); if(!password) { - retcode = -1; /* allocation failed */ + retcode = NETRC_FAILED; /* allocation failed */ goto out; } password_alloc = TRUE; @@ -215,6 +173,7 @@ int Curl_parsenetrc(const char *host, out: if(!retcode) { + /* success */ *login_changed = FALSE; *password_changed = FALSE; if(login_alloc) { @@ -242,4 +201,78 @@ int Curl_parsenetrc(const char *host, return retcode; } +/* + * @unittest: 1304 + * + * *loginp and *passwordp MUST be allocated if they aren't NULL when passed + * in. + */ +int Curl_parsenetrc(const char *host, + char **loginp, + char **passwordp, + bool *login_changed, + bool *password_changed, + char *netrcfile) +{ + int retcode = 1; + char *filealloc = NULL; + + if(!netrcfile) { + char *home = NULL; + char *homea = curl_getenv("HOME"); /* portable environment reader */ + if(homea) { + home = homea; +#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) + } + else { + struct passwd pw, *pw_res; + char pwbuf[1024]; + if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) + && pw_res) { + home = pw.pw_dir; + } +#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID) + } + else { + struct passwd *pw; + pw = getpwuid(geteuid()); + if(pw) { + home = pw->pw_dir; + } +#endif + } + + if(!home) + return retcode; /* no home directory found (or possibly out of + memory) */ + + filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); + if(!filealloc) { + free(homea); + return -1; + } + retcode = parsenetrc(host, loginp, passwordp, login_changed, + password_changed, filealloc); + free(filealloc); +#ifdef WIN32 + if(retcode == NETRC_FILE_MISSING) { + /* fallback to the old-style "_netrc" file */ + filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR); + if(!filealloc) { + free(homea); + return -1; + } + retcode = parsenetrc(host, loginp, passwordp, login_changed, + password_changed, filealloc); + free(filealloc); + } +#endif + free(homea); + } + else + retcode = parsenetrc(host, loginp, passwordp, login_changed, + password_changed, netrcfile); + return retcode; +} + #endif diff --git a/Utilities/cmcurl/lib/non-ascii.c b/Utilities/cmcurl/lib/non-ascii.c index 42beaec45de..a48e67db4ad 100644 --- a/Utilities/cmcurl/lib/non-ascii.c +++ b/Utilities/cmcurl/lib/non-ascii.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/nonblock.c b/Utilities/cmcurl/lib/nonblock.c index 4d105c1fea8..abeb6598c25 100644 --- a/Utilities/cmcurl/lib/nonblock.c +++ b/Utilities/cmcurl/lib/nonblock.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/nonblock.h b/Utilities/cmcurl/lib/nonblock.h index eb18ea1c34d..d50d3159446 100644 --- a/Utilities/cmcurl/lib/nonblock.h +++ b/Utilities/cmcurl/lib/nonblock.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/nwlib.c b/Utilities/cmcurl/lib/nwlib.c index 7bf5f51c36a..beec0b3889d 100644 --- a/Utilities/cmcurl/lib/nwlib.c +++ b/Utilities/cmcurl/lib/nwlib.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -36,21 +36,19 @@ /* The last #include file should be: */ #include "memdebug.h" -typedef struct -{ +struct libthreaddata { int _errno; void *twentybytes; -} libthreaddata_t; +}; -typedef struct -{ +struct libdata { int x; int y; int z; void *tenbytes; NXKey_t perthreadkey; /* if -1, no key obtained... */ NXMutex_t *lock; -} libdata_t; +}; int gLibId = -1; void *gLibHandle = (void *) NULL; @@ -60,7 +58,8 @@ NXMutex_t *gLibLock = (NXMutex_t *) NULL; /* internal library function prototypes... */ int DisposeLibraryData(void *); void DisposeThreadData(void *); -int GetOrSetUpData(int id, libdata_t **data, libthreaddata_t **threaddata); +int GetOrSetUpData(int id, struct libdata **data, + struct libthreaddata **threaddata); int _NonAppStart(void *NLMHandle, @@ -154,24 +153,24 @@ int _NonAppCheckUnload(void) return 0; } -int GetOrSetUpData(int id, libdata_t **appData, - libthreaddata_t **threadData) +int GetOrSetUpData(int id, struct libdata **appData, + struct libthreaddata **threadData) { int err; - libdata_t *app_data; - libthreaddata_t *thread_data; + struct libdata *app_data; + struct libthreaddata *thread_data; NXKey_t key; NX_LOCK_INFO_ALLOC(liblock, "Application Data Lock", 0); err = 0; - thread_data = (libthreaddata_t *) NULL; + thread_data = (struct libthreaddata_t *) NULL; /* * Attempt to get our data for the application calling us. This is where we * store whatever application-specific information we need to carry in * support of calling applications. */ - app_data = (libdata_t *) get_app_data(id); + app_data = (struct libdata *) get_app_data(id); if(!app_data) { /* @@ -184,9 +183,9 @@ int GetOrSetUpData(int id, libdata_t **appData, */ NXLock(gLibLock); - app_data = (libdata_t *) get_app_data(id); + app_data = (struct libdata *) get_app_data(id); if(!app_data) { - app_data = calloc(1, sizeof(libdata_t)); + app_data = calloc(1, sizeof(struct libdata)); if(app_data) { app_data->tenbytes = malloc(10); @@ -249,7 +248,7 @@ int GetOrSetUpData(int id, libdata_t **appData, * a pointer is not very important, this just helps to demonstrate that * we can have arbitrarily complex per-thread data. */ - thread_data = malloc(sizeof(libthreaddata_t)); + thread_data = malloc(sizeof(struct libthreaddata)); if(thread_data) { thread_data->_errno = 0; @@ -257,7 +256,7 @@ int GetOrSetUpData(int id, libdata_t **appData, if(!thread_data->twentybytes) { free(thread_data); - thread_data = (libthreaddata_t *) NULL; + thread_data = (struct libthreaddata *) NULL; err = ENOMEM; } @@ -265,7 +264,7 @@ int GetOrSetUpData(int id, libdata_t **appData, if(err) { free(thread_data->twentybytes); free(thread_data); - thread_data = (libthreaddata_t *) NULL; + thread_data = (struct libthreaddata *) NULL; } } } @@ -295,7 +294,7 @@ int DisposeLibraryData(void *data) void DisposeThreadData(void *data) { if(data) { - void *twentybytes = ((libthreaddata_t *) data)->twentybytes; + void *twentybytes = ((struct libthreaddata *) data)->twentybytes; free(twentybytes); free(data); diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index eeab2c7a78b..782d6a08e7a 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2010, Howard Chu, - * Copyright (C) 2011 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2011 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -151,7 +151,7 @@ static const char *url_errs[] = { "bad or missing extensions" }; -typedef struct ldapconninfo { +struct ldapconninfo { LDAP *ld; Curl_recv *recv; /* for stacking SSL handler */ Curl_send *send; @@ -160,16 +160,16 @@ typedef struct ldapconninfo { bool ssldone; bool sslinst; bool didbind; -} ldapconninfo; +}; -typedef struct ldapreqinfo { +struct ldapreqinfo { int msgid; int nument; -} ldapreqinfo; +}; static CURLcode ldap_setup_connection(struct connectdata *conn) { - ldapconninfo *li; + struct ldapconninfo *li; LDAPURLDesc *lud; struct Curl_easy *data = conn->data; int rc, proto; @@ -190,11 +190,11 @@ static CURLcode ldap_setup_connection(struct connectdata *conn) proto = ldap_pvt_url_scheme2proto(lud->lud_scheme); ldap_free_urldesc(lud); - li = calloc(1, sizeof(ldapconninfo)); + li = calloc(1, sizeof(struct ldapconninfo)); if(!li) return CURLE_OUT_OF_MEMORY; li->proto = proto; - conn->proto.generic = li; + conn->proto.ldapc = li; connkeep(conn, "OpenLDAP default"); return CURLE_OK; } @@ -205,7 +205,7 @@ static Sockbuf_IO ldapsb_tls; static CURLcode ldap_connect(struct connectdata *conn, bool *done) { - ldapconninfo *li = conn->proto.generic; + struct ldapconninfo *li = conn->proto.ldapc; struct Curl_easy *data = conn->data; int rc, proto = LDAP_VERSION3; char hosturl[1024]; @@ -252,7 +252,7 @@ static CURLcode ldap_connect(struct connectdata *conn, bool *done) static CURLcode ldap_connecting(struct connectdata *conn, bool *done) { - ldapconninfo *li = conn->proto.generic; + struct ldapconninfo *li = conn->proto.ldapc; struct Curl_easy *data = conn->data; LDAPMessage *msg = NULL; struct timeval tv = {0, 1}, *tvp; @@ -357,7 +357,7 @@ static CURLcode ldap_connecting(struct connectdata *conn, bool *done) static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection) { - ldapconninfo *li = conn->proto.generic; + struct ldapconninfo *li = conn->proto.ldapc; (void) dead_connection; if(li) { @@ -365,7 +365,7 @@ static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection) ldap_unbind_ext(li->ld, NULL, NULL); li->ld = NULL; } - conn->proto.generic = NULL; + conn->proto.ldapc = NULL; free(li); } return CURLE_OK; @@ -373,8 +373,8 @@ static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection) static CURLcode ldap_do(struct connectdata *conn, bool *done) { - ldapconninfo *li = conn->proto.generic; - ldapreqinfo *lr; + struct ldapconninfo *li = conn->proto.ldapc; + struct ldapreqinfo *lr; CURLcode status = CURLE_OK; int rc = 0; LDAPURLDesc *ludp = NULL; @@ -406,7 +406,7 @@ static CURLcode ldap_do(struct connectdata *conn, bool *done) failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc)); return CURLE_LDAP_SEARCH_FAILED; } - lr = calloc(1, sizeof(ldapreqinfo)); + lr = calloc(1, sizeof(struct ldapreqinfo)); if(!lr) return CURLE_OUT_OF_MEMORY; lr->msgid = msgid; @@ -419,7 +419,7 @@ static CURLcode ldap_do(struct connectdata *conn, bool *done) static CURLcode ldap_done(struct connectdata *conn, CURLcode res, bool premature) { - ldapreqinfo *lr = conn->data->req.protop; + struct ldapreqinfo *lr = conn->data->req.protop; (void)res; (void)premature; @@ -427,7 +427,7 @@ static CURLcode ldap_done(struct connectdata *conn, CURLcode res, if(lr) { /* if there was a search in progress, abandon it */ if(lr->msgid) { - ldapconninfo *li = conn->proto.generic; + struct ldapconninfo *li = conn->proto.ldapc; ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL); lr->msgid = 0; } @@ -441,9 +441,9 @@ static CURLcode ldap_done(struct connectdata *conn, CURLcode res, static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf, size_t len, CURLcode *err) { - ldapconninfo *li = conn->proto.generic; + struct ldapconninfo *li = conn->proto.ldapc; struct Curl_easy *data = conn->data; - ldapreqinfo *lr = data->req.protop; + struct ldapreqinfo *lr = data->req.protop; int rc, ret; LDAPMessage *msg = NULL; LDAPMessage *ent; @@ -718,7 +718,7 @@ static ber_slen_t ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) { struct connectdata *conn = sbiod->sbiod_pvt; - ldapconninfo *li = conn->proto.generic; + struct ldapconninfo *li = conn->proto.ldapc; ber_slen_t ret; CURLcode err = CURLE_RECV_ERROR; @@ -733,7 +733,7 @@ static ber_slen_t ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) { struct connectdata *conn = sbiod->sbiod_pvt; - ldapconninfo *li = conn->proto.generic; + struct ldapconninfo *li = conn->proto.ldapc; ber_slen_t ret; CURLcode err = CURLE_SEND_ERROR; diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c index 7ae5eb8cded..585d7ea404d 100644 --- a/Utilities/cmcurl/lib/parsedate.c +++ b/Utilities/cmcurl/lib/parsedate.c @@ -100,16 +100,20 @@ static int parsedate(const char *date, time_t *output); #define PARSEDATE_LATER 1 #define PARSEDATE_SOONER 2 -#ifndef CURL_DISABLE_PARSEDATE - +#if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \ + !defined(CURL_DISABLE_FILE) +/* These names are also used by FTP and FILE code */ const char * const Curl_wkday[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; -static const char * const weekday[] = -{ "Monday", "Tuesday", "Wednesday", "Thursday", - "Friday", "Saturday", "Sunday" }; const char * const Curl_month[]= { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +#endif + +#ifndef CURL_DISABLE_PARSEDATE +static const char * const weekday[] = +{ "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday", "Sunday" }; struct tzinfo { char name[5]; @@ -583,6 +587,30 @@ time_t curl_getdate(const char *p, const time_t *now) return -1; } +/* Curl_getdate_capped() differs from curl_getdate() in that this will return + TIME_T_MAX in case the parsed time value was too big, instead of an + error. */ + +time_t Curl_getdate_capped(const char *p) +{ + time_t parsed = -1; + int rc = parsedate(p, &parsed); + + switch(rc) { + case PARSEDATE_OK: + if(parsed == -1) + /* avoid returning -1 for a working scenario */ + parsed++; + return parsed; + case PARSEDATE_LATER: + /* this returns the maximum time value */ + return parsed; + default: + return -1; /* everything else is fail */ + } + /* UNREACHABLE */ +} + /* * Curl_gmtime() is a gmtime() replacement for portability. Do not use the * gmtime_r() or gmtime() functions anywhere else but here. diff --git a/Utilities/cmcurl/lib/parsedate.h b/Utilities/cmcurl/lib/parsedate.h index 8dc3b90ec76..8c7ae94e438 100644 --- a/Utilities/cmcurl/lib/parsedate.h +++ b/Utilities/cmcurl/lib/parsedate.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,4 +27,10 @@ extern const char * const Curl_month[12]; CURLcode Curl_gmtime(time_t intime, struct tm *store); +/* Curl_getdate_capped() differs from curl_getdate() in that this will return + TIME_T_MAX in case the parsed time value was too big, instead of an + error. */ + +time_t Curl_getdate_capped(const char *p); + #endif /* HEADER_CURL_PARSEDATE_H */ diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c index e9568ee3de0..3143315219e 100644 --- a/Utilities/cmcurl/lib/pingpong.c +++ b/Utilities/cmcurl/lib/pingpong.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -44,12 +44,12 @@ /* Returns timeout in ms. 0 or negative number means the timeout has already triggered */ -time_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting) +timediff_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting) { struct connectdata *conn = pp->conn; struct Curl_easy *data = conn->data; - time_t timeout_ms; /* in milliseconds */ - long response_time = (data->set.server_response_timeout)? + timediff_t timeout_ms; /* in milliseconds */ + timediff_t response_time = (data->set.server_response_timeout)? data->set.server_response_timeout: pp->response_time; /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine @@ -64,7 +64,7 @@ time_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting) if(data->set.timeout && !disconnecting) { /* if timeout is requested, find out how much remaining time we have */ - time_t timeout2_ms = data->set.timeout - /* timeout time */ + timediff_t timeout2_ms = data->set.timeout - /* timeout time */ Curl_timediff(Curl_now(), conn->now); /* spent time */ /* pick the lowest number */ @@ -83,8 +83,8 @@ CURLcode Curl_pp_statemach(struct pingpong *pp, bool block, struct connectdata *conn = pp->conn; curl_socket_t sock = conn->sock[FIRSTSOCKET]; int rc; - time_t interval_ms; - time_t timeout_ms = Curl_pp_state_timeout(pp, disconnecting); + timediff_t interval_ms; + timediff_t timeout_ms = Curl_pp_state_timeout(pp, disconnecting); struct Curl_easy *data = conn->data; CURLcode result = CURLE_OK; @@ -384,10 +384,10 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd, if(pp->endofresp(conn, pp->linestart_resp, perline, code)) { /* This is the end of the last line, copy the last line to the - start of the buffer and zero terminate, for old times sake */ + start of the buffer and null-terminate, for old times sake */ size_t n = ptr - pp->linestart_resp; memmove(buf, pp->linestart_resp, n); - buf[n] = 0; /* zero terminate */ + buf[n] = 0; /* null-terminate */ keepon = FALSE; pp->linestart_resp = ptr + 1; /* advance pointer */ i++; /* skip this before getting out */ @@ -463,14 +463,9 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd, } int Curl_pp_getsock(struct pingpong *pp, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { struct connectdata *conn = pp->conn; - - if(!numsocks) - return GETSOCK_BLANK; - socks[0] = conn->sock[FIRSTSOCKET]; if(pp->sendleft) { diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h index dbe1f8d3d73..e874799d441 100644 --- a/Utilities/cmcurl/lib/pingpong.h +++ b/Utilities/cmcurl/lib/pingpong.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -60,9 +60,8 @@ struct pingpong { size_t sendsize; /* total size of the sendthis buffer */ struct curltime response; /* set to Curl_now() when a command has been sent off, used to time-out response reading */ - long response_time; /* When no timeout is given, this is the amount of - milliseconds we await for a server response. */ - + timediff_t response_time; /* When no timeout is given, this is the amount of + milliseconds we await for a server response. */ struct connectdata *conn; /* points to the connectdata struct that this belongs to */ @@ -89,7 +88,7 @@ void Curl_pp_init(struct pingpong *pp); /* Returns timeout in ms. 0 or negative number means the timeout has already triggered */ -time_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting); +timediff_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting); /*********************************************************************** @@ -135,8 +134,7 @@ CURLcode Curl_pp_flushsend(struct pingpong *pp); /* call this when a pingpong connection is disconnected */ CURLcode Curl_pp_disconnect(struct pingpong *pp); -int Curl_pp_getsock(struct pingpong *pp, curl_socket_t *socks, - int numsocks); +int Curl_pp_getsock(struct pingpong *pp, curl_socket_t *socks); /*********************************************************************** diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index c8f3965e494..9ff5c78fd09 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -95,8 +95,7 @@ static CURLcode pop3_done(struct connectdata *conn, CURLcode status, static CURLcode pop3_connect(struct connectdata *conn, bool *done); static CURLcode pop3_disconnect(struct connectdata *conn, bool dead); static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done); -static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); +static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks); static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done); static CURLcode pop3_setup_connection(struct connectdata *conn); static CURLcode pop3_parse_url_options(struct connectdata *conn); @@ -179,7 +178,7 @@ static void pop3_to_pop3s(struct connectdata *conn) conn->handler = &Curl_handler_pop3s; /* Set the connection's upgraded to TLS flag */ - conn->tls_upgraded = TRUE; + conn->bits.tls_upgraded = TRUE; } #else #define pop3_to_pop3s(x) Curl_nop_stmt @@ -339,10 +338,8 @@ static CURLcode pop3_perform_capa(struct connectdata *conn) */ static CURLcode pop3_perform_starttls(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* Send the STLS command */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS"); + CURLcode result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS"); if(!result) state(conn, POP3_STARTTLS); @@ -358,11 +355,10 @@ static CURLcode pop3_perform_starttls(struct connectdata *conn) */ static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn) { - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; - /* Start the SSL connection */ - result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); + struct pop3_conn *pop3c = &conn->proto.pop3c; + CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, + &pop3c->ssldone); if(!result) { if(pop3c->state != POP3_UPGRADETLS) @@ -416,7 +412,7 @@ static CURLcode pop3_perform_apop(struct connectdata *conn) CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; size_t i; - MD5_context *ctxt; + struct MD5_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; char secret[2 * MD5_DIGEST_LEN + 1]; @@ -593,10 +589,8 @@ static CURLcode pop3_perform_command(struct connectdata *conn) */ static CURLcode pop3_perform_quit(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* Send the QUIT command */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT"); + CURLcode result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT"); if(!result) state(conn, POP3_QUIT); @@ -1060,10 +1054,9 @@ static CURLcode pop3_init(struct connectdata *conn) } /* For the POP3 "protocol connect" and "doing" phases only */ -static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks) { - return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks); + return Curl_pp_getsock(&conn->proto.pop3c.pp, socks); } /*********************************************************************** @@ -1319,7 +1312,7 @@ static CURLcode pop3_setup_connection(struct connectdata *conn) return result; /* Clear the TLS upgraded flag */ - conn->tls_upgraded = FALSE; + conn->bits.tls_upgraded = FALSE; return CURLE_OK; } @@ -1397,7 +1390,7 @@ static CURLcode pop3_parse_url_path(struct connectdata *conn) const char *path = &data->state.up.path[1]; /* skip leading path */ /* URL decode the path for the message ID */ - return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE); + return Curl_urldecode(data, path, 0, &pop3->id, NULL, REJECT_CTRL); } /*********************************************************************** @@ -1415,7 +1408,7 @@ static CURLcode pop3_parse_custom_request(struct connectdata *conn) /* URL decode the custom request */ if(custom) - result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE); + result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, REJECT_CTRL); return result; } diff --git a/Utilities/cmcurl/lib/pop3.h b/Utilities/cmcurl/lib/pop3.h index a8e697cde2b..3ba79997711 100644 --- a/Utilities/cmcurl/lib/pop3.h +++ b/Utilities/cmcurl/lib/pop3.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2009 - 2015, Daniel Stenberg, , et al. + * Copyright (C) 2009 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c index f586d59b4cf..8951384481a 100644 --- a/Utilities/cmcurl/lib/progress.c +++ b/Utilities/cmcurl/lib/progress.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,7 @@ #include "sendf.h" #include "multiif.h" #include "progress.h" +#include "timeval.h" #include "curl_printf.h" /* check rate limits within this many recent milliseconds, at minimum. */ @@ -168,7 +169,7 @@ void Curl_pgrsResetTransferSizes(struct Curl_easy *data) void Curl_pgrsTime(struct Curl_easy *data, timerid timer) { struct curltime now = Curl_now(); - time_t *delta = NULL; + timediff_t *delta = NULL; switch(timer) { default: @@ -238,6 +239,8 @@ void Curl_pgrsStartNow(struct Curl_easy *data) data->progress.ul_limit_start.tv_usec = 0; data->progress.dl_limit_start.tv_sec = 0; data->progress.dl_limit_start.tv_usec = 0; + data->progress.downloaded = 0; + data->progress.uploaded = 0; /* clear all bits except HIDE and HEADERS_OUT */ data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT; Curl_ratelimit(data, data->progress.start); @@ -268,8 +271,8 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, struct curltime now) { curl_off_t size = cursize - startsize; - time_t minimum; - time_t actual; + timediff_t minimum; + timediff_t actual; if(!limit || !size) return 0; @@ -279,13 +282,13 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, * stay below 'limit'. */ if(size < CURL_OFF_T_MAX/1000) - minimum = (time_t) (CURL_OFF_T_C(1000) * size / limit); + minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / limit); else { - minimum = (time_t) (size / limit); - if(minimum < TIME_T_MAX/1000) + minimum = (timediff_t) (size / limit); + if(minimum < TIMEDIFF_T_MAX/1000) minimum *= 1000; else - minimum = TIME_T_MAX; + minimum = TIMEDIFF_T_MAX; } /* @@ -364,18 +367,15 @@ void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size) } } -#ifndef CURL_DISABLE_PROGRESS_METER -static void progress_meter(struct connectdata *conn) +/* returns TRUE if it's time to show the progress meter */ +static bool progress_calc(struct connectdata *conn, struct curltime now) { - struct curltime now; curl_off_t timespent; curl_off_t timespent_ms; /* milliseconds */ struct Curl_easy *data = conn->data; - bool shownow = FALSE; curl_off_t dl = data->progress.downloaded; curl_off_t ul = data->progress.uploaded; - - now = Curl_now(); /* what time is it */ + bool timetoshow = FALSE; /* The time spent so far (from the start) */ data->progress.timespent = Curl_timediff_us(now, data->progress.start); @@ -398,10 +398,8 @@ static void progress_meter(struct connectdata *conn) if(data->progress.lastshow != now.tv_sec) { int countindex; /* amount of seconds stored in the speeder array */ int nowindex = data->progress.speeder_c% CURR_TIME; - if(!(data->progress.flags & PGRS_HIDE)) - shownow = TRUE; - data->progress.lastshow = now.tv_sec; + timetoshow = TRUE; /* Let's do the "current speed" thing, with the dl + ul speeds combined. Store the speed at entry 'nowindex'. */ @@ -434,8 +432,7 @@ static void progress_meter(struct connectdata *conn) data->progress.speeder_c%CURR_TIME:0; /* Figure out the exact time for the time span */ - span_ms = Curl_timediff(now, - data->progress.speeder_time[checkindex]); + span_ms = Curl_timediff(now, data->progress.speeder_time[checkindex]); if(0 == span_ms) span_ms = 1; /* at least one millisecond MUST have passed */ @@ -461,118 +458,119 @@ static void progress_meter(struct connectdata *conn) data->progress.ulspeed + data->progress.dlspeed; } /* Calculations end */ - if(!shownow) - /* only show the internal progress meter once per second */ - return; - else { - /* If there's no external callback set, use internal code to show - progress */ - /* progress meter has not been shut off */ - char max5[6][10]; - curl_off_t dlpercen = 0; - curl_off_t ulpercen = 0; - curl_off_t total_percen = 0; - curl_off_t total_transfer; - curl_off_t total_expected_transfer; - char time_left[10]; - char time_total[10]; - char time_spent[10]; - curl_off_t ulestimate = 0; - curl_off_t dlestimate = 0; - curl_off_t total_estimate; - - if(!(data->progress.flags & PGRS_HEADERS_OUT)) { - if(data->state.resume_from) { - fprintf(data->set.err, - "** Resuming transfer from byte position %" - CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from); - } - fprintf(data->set.err, - " %% Total %% Received %% Xferd Average Speed " - "Time Time Time Current\n" - " Dload Upload " - "Total Spent Left Speed\n"); - data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */ - } - - /* Figure out the estimated time of arrival for the upload */ - if((data->progress.flags & PGRS_UL_SIZE_KNOWN) && - (data->progress.ulspeed > CURL_OFF_T_C(0))) { - ulestimate = data->progress.size_ul / data->progress.ulspeed; - - if(data->progress.size_ul > CURL_OFF_T_C(10000)) - ulpercen = data->progress.uploaded / - (data->progress.size_ul/CURL_OFF_T_C(100)); - else if(data->progress.size_ul > CURL_OFF_T_C(0)) - ulpercen = (data->progress.uploaded*100) / - data->progress.size_ul; - } + return timetoshow; +} - /* ... and the download */ - if((data->progress.flags & PGRS_DL_SIZE_KNOWN) && - (data->progress.dlspeed > CURL_OFF_T_C(0))) { - dlestimate = data->progress.size_dl / data->progress.dlspeed; - - if(data->progress.size_dl > CURL_OFF_T_C(10000)) - dlpercen = data->progress.downloaded / - (data->progress.size_dl/CURL_OFF_T_C(100)); - else if(data->progress.size_dl > CURL_OFF_T_C(0)) - dlpercen = (data->progress.downloaded*100) / - data->progress.size_dl; +#ifndef CURL_DISABLE_PROGRESS_METER +static void progress_meter(struct connectdata *conn) +{ + struct Curl_easy *data = conn->data; + char max5[6][10]; + curl_off_t dlpercen = 0; + curl_off_t ulpercen = 0; + curl_off_t total_percen = 0; + curl_off_t total_transfer; + curl_off_t total_expected_transfer; + char time_left[10]; + char time_total[10]; + char time_spent[10]; + curl_off_t ulestimate = 0; + curl_off_t dlestimate = 0; + curl_off_t total_estimate; + curl_off_t timespent = + (curl_off_t)data->progress.timespent/1000000; /* seconds */ + + if(!(data->progress.flags & PGRS_HEADERS_OUT)) { + if(data->state.resume_from) { + fprintf(data->set.err, + "** Resuming transfer from byte position %" + CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from); } + fprintf(data->set.err, + " %% Total %% Received %% Xferd Average Speed " + "Time Time Time Current\n" + " Dload Upload " + "Total Spent Left Speed\n"); + data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */ + } - /* Now figure out which of them is slower and use that one for the - total estimate! */ - total_estimate = ulestimate>dlestimate?ulestimate:dlestimate; - - /* create the three time strings */ - time2str(time_left, total_estimate > 0?(total_estimate - timespent):0); - time2str(time_total, total_estimate); - time2str(time_spent, timespent); - - /* Get the total amount of data expected to get transferred */ - total_expected_transfer = - ((data->progress.flags & PGRS_UL_SIZE_KNOWN)? - data->progress.size_ul:data->progress.uploaded)+ - ((data->progress.flags & PGRS_DL_SIZE_KNOWN)? - data->progress.size_dl:data->progress.downloaded); - - /* We have transferred this much so far */ - total_transfer = data->progress.downloaded + data->progress.uploaded; + /* Figure out the estimated time of arrival for the upload */ + if((data->progress.flags & PGRS_UL_SIZE_KNOWN) && + (data->progress.ulspeed > CURL_OFF_T_C(0))) { + ulestimate = data->progress.size_ul / data->progress.ulspeed; + + if(data->progress.size_ul > CURL_OFF_T_C(10000)) + ulpercen = data->progress.uploaded / + (data->progress.size_ul/CURL_OFF_T_C(100)); + else if(data->progress.size_ul > CURL_OFF_T_C(0)) + ulpercen = (data->progress.uploaded*100) / + data->progress.size_ul; + } - /* Get the percentage of data transferred so far */ - if(total_expected_transfer > CURL_OFF_T_C(10000)) - total_percen = total_transfer / - (total_expected_transfer/CURL_OFF_T_C(100)); - else if(total_expected_transfer > CURL_OFF_T_C(0)) - total_percen = (total_transfer*100) / total_expected_transfer; + /* ... and the download */ + if((data->progress.flags & PGRS_DL_SIZE_KNOWN) && + (data->progress.dlspeed > CURL_OFF_T_C(0))) { + dlestimate = data->progress.size_dl / data->progress.dlspeed; + + if(data->progress.size_dl > CURL_OFF_T_C(10000)) + dlpercen = data->progress.downloaded / + (data->progress.size_dl/CURL_OFF_T_C(100)); + else if(data->progress.size_dl > CURL_OFF_T_C(0)) + dlpercen = (data->progress.downloaded*100) / + data->progress.size_dl; + } - fprintf(data->set.err, - "\r" - "%3" CURL_FORMAT_CURL_OFF_T " %s " - "%3" CURL_FORMAT_CURL_OFF_T " %s " - "%3" CURL_FORMAT_CURL_OFF_T " %s %s %s %s %s %s %s", - total_percen, /* 3 letters */ /* total % */ - max5data(total_expected_transfer, max5[2]), /* total size */ - dlpercen, /* 3 letters */ /* rcvd % */ - max5data(data->progress.downloaded, max5[0]), /* rcvd size */ - ulpercen, /* 3 letters */ /* xfer % */ - max5data(data->progress.uploaded, max5[1]), /* xfer size */ - max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */ - max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */ - time_total, /* 8 letters */ /* total time */ - time_spent, /* 8 letters */ /* time spent */ - time_left, /* 8 letters */ /* time left */ - max5data(data->progress.current_speed, max5[5]) - ); - - /* we flush the output stream to make it appear as soon as possible */ - fflush(data->set.err); - } /* don't show now */ + /* Now figure out which of them is slower and use that one for the + total estimate! */ + total_estimate = ulestimate>dlestimate?ulestimate:dlestimate; + + /* create the three time strings */ + time2str(time_left, total_estimate > 0?(total_estimate - timespent):0); + time2str(time_total, total_estimate); + time2str(time_spent, timespent); + + /* Get the total amount of data expected to get transferred */ + total_expected_transfer = + ((data->progress.flags & PGRS_UL_SIZE_KNOWN)? + data->progress.size_ul:data->progress.uploaded)+ + ((data->progress.flags & PGRS_DL_SIZE_KNOWN)? + data->progress.size_dl:data->progress.downloaded); + + /* We have transferred this much so far */ + total_transfer = data->progress.downloaded + data->progress.uploaded; + + /* Get the percentage of data transferred so far */ + if(total_expected_transfer > CURL_OFF_T_C(10000)) + total_percen = total_transfer / + (total_expected_transfer/CURL_OFF_T_C(100)); + else if(total_expected_transfer > CURL_OFF_T_C(0)) + total_percen = (total_transfer*100) / total_expected_transfer; + + fprintf(data->set.err, + "\r" + "%3" CURL_FORMAT_CURL_OFF_T " %s " + "%3" CURL_FORMAT_CURL_OFF_T " %s " + "%3" CURL_FORMAT_CURL_OFF_T " %s %s %s %s %s %s %s", + total_percen, /* 3 letters */ /* total % */ + max5data(total_expected_transfer, max5[2]), /* total size */ + dlpercen, /* 3 letters */ /* rcvd % */ + max5data(data->progress.downloaded, max5[0]), /* rcvd size */ + ulpercen, /* 3 letters */ /* xfer % */ + max5data(data->progress.uploaded, max5[1]), /* xfer size */ + max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */ + max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */ + time_total, /* 8 letters */ /* total time */ + time_spent, /* 8 letters */ /* time spent */ + time_left, /* 8 letters */ /* time left */ + max5data(data->progress.current_speed, max5[5]) + ); + + /* we flush the output stream to make it appear as soon as possible */ + fflush(data->set.err); } #else /* progress bar disabled */ -#define progress_meter(x) +#define progress_meter(x) Curl_nop_stmt #endif @@ -583,6 +581,8 @@ static void progress_meter(struct connectdata *conn) int Curl_pgrsUpdate(struct connectdata *conn) { struct Curl_easy *data = conn->data; + struct curltime now = Curl_now(); /* what time is it */ + bool showprogress = progress_calc(conn, now); if(!(data->progress.flags & PGRS_HIDE)) { if(data->set.fxferinfo) { int result; @@ -594,11 +594,13 @@ int Curl_pgrsUpdate(struct connectdata *conn) data->progress.size_ul, data->progress.uploaded); Curl_set_in_callback(data, false); - if(result) - failf(data, "Callback aborted"); - return result; + if(result != CURL_PROGRESSFUNC_CONTINUE) { + if(result) + failf(data, "Callback aborted"); + return result; + } } - if(data->set.fprogress) { + else if(data->set.fprogress) { int result; /* The older deprecated callback is set, call that */ Curl_set_in_callback(data, true); @@ -608,12 +610,16 @@ int Curl_pgrsUpdate(struct connectdata *conn) (double)data->progress.size_ul, (double)data->progress.uploaded); Curl_set_in_callback(data, false); - if(result) - failf(data, "Callback aborted"); - return result; + if(result != CURL_PROGRESSFUNC_CONTINUE) { + if(result) + failf(data, "Callback aborted"); + return result; + } } + + if(showprogress) + progress_meter(conn); } - progress_meter(conn); return 0; } diff --git a/Utilities/cmcurl/lib/quic.h b/Utilities/cmcurl/lib/quic.h new file mode 100644 index 00000000000..8e7df90eac0 --- /dev/null +++ b/Utilities/cmcurl/lib/quic.h @@ -0,0 +1,59 @@ +#ifndef HEADER_CURL_QUIC_H +#define HEADER_CURL_QUIC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef ENABLE_QUIC +#ifdef USE_NGTCP2 +#include "vquic/ngtcp2.h" +#endif +#ifdef USE_QUICHE +#include "vquic/quiche.h" +#endif + +#include "urldata.h" + +/* functions provided by the specific backends */ +CURLcode Curl_quic_connect(struct connectdata *conn, + curl_socket_t sockfd, + int sockindex, + const struct sockaddr *addr, + socklen_t addrlen); +CURLcode Curl_quic_is_connected(struct connectdata *conn, + curl_socket_t sockfd, + bool *connected); +int Curl_quic_ver(char *p, size_t len); +CURLcode Curl_quic_done_sending(struct connectdata *conn); +void Curl_quic_done(struct Curl_easy *data, bool premature); +bool Curl_quic_data_pending(const struct Curl_easy *data); +void Curl_quic_disconnect(struct connectdata *conn, int tempindex); + +#else /* ENABLE_QUIC */ +#define Curl_quic_done_sending(x) +#define Curl_quic_done(x,y) +#define Curl_quic_data_pending(x) +#define Curl_quic_disconnect(x,y) +#endif /* !ENABLE_QUIC */ + +#endif /* HEADER_CURL_QUIC_H */ diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index 6ee45feb121..c415048ec5a 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -106,8 +106,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) * 'rndptr' points to. * * If libcurl is built without TLS support or with a TLS backend that lacks a - * proper random API (Gskit, PolarSSL or mbedTLS), this function will use - * "weak" random. + * proper random API (Gskit or mbedTLS), this function will use "weak" random. * * When built *with* TLS support and a backend that offers strong random, it * will return error if it cannot provide strong random values. diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h index 5deb0416137..3c8e2b81dc9 100644 --- a/Utilities/cmcurl/lib/rand.h +++ b/Utilities/cmcurl/lib/rand.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,8 +27,7 @@ * 'rnd' points to. * * If libcurl is built without TLS support or with a TLS backend that lacks a - * proper random API (Gskit, PolarSSL or mbedTLS), this function will use - * "weak" random. + * proper random API (Gskit or mbedTLS), this function will use "weak" random. * * When built *with* TLS support and a backend that offers strong random, it * will return error if it cannot provide strong random values. diff --git a/Utilities/cmcurl/lib/rename.c b/Utilities/cmcurl/lib/rename.c new file mode 100644 index 00000000000..bb170d3cc7b --- /dev/null +++ b/Utilities/cmcurl/lib/rename.c @@ -0,0 +1,62 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "rename.h" + +#include "curl_setup.h" + +#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)) || \ + defined(USE_ALTSVC) + +#include "timeval.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* return 0 on success, 1 on error */ +int Curl_rename(const char *oldpath, const char *newpath) +{ +#ifdef WIN32 + /* rename() on Windows doesn't overwrite, so we can't use it here. + MoveFileExA() will overwrite and is usually atomic, however it fails + when there are open handles to the file. */ + const int max_wait_ms = 1000; + struct curltime start = Curl_now(); + for(;;) { + timediff_t diff; + if(MoveFileExA(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) + break; + diff = Curl_timediff(Curl_now(), start); + if(diff < 0 || diff > max_wait_ms) + return 1; + Sleep(1); + } +#else + if(rename(oldpath, newpath)) + return 1; +#endif + return 0; +} + +#endif diff --git a/Utilities/cmcurl/lib/rename.h b/Utilities/cmcurl/lib/rename.h new file mode 100644 index 00000000000..d7442c8447c --- /dev/null +++ b/Utilities/cmcurl/lib/rename.h @@ -0,0 +1,27 @@ +#ifndef HEADER_CURL_RENAME_H +#define HEADER_CURL_RENAME_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +int Curl_rename(const char *oldpath, const char *newpath); + +#endif /* HEADER_CURL_RENAME_H */ diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index 74cf232448c..dbd7dc6a6db 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -52,10 +52,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done); static CURLcode rtsp_done(struct connectdata *conn, CURLcode, bool premature); static CURLcode rtsp_connect(struct connectdata *conn, bool *done); static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead); - -static int rtsp_getsock_do(struct connectdata *conn, - curl_socket_t *socks, - int numsocks); +static int rtsp_getsock_do(struct connectdata *conn, curl_socket_t *socks); /* * Parse and write out any available RTP data. @@ -77,11 +74,9 @@ static unsigned int rtsp_conncheck(struct connectdata *check, interface and then we're always _sending_ a request and thus we wait for the single socket to become writable only */ static int rtsp_getsock_do(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { /* write mode */ - (void)numsocks; /* unused, we trust it to be at least 1 */ socks[0] = conn->sock[FIRSTSOCKET]; return GETSOCK_WRITESOCK(0); } @@ -238,7 +233,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) CURLcode result = CURLE_OK; Curl_RtspReq rtspreq = data->set.rtspreq; struct RTSP *rtsp = data->req.protop; - Curl_send_buffer *req_buffer; + struct dynbuf req_buffer; curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */ curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */ @@ -338,12 +333,12 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(rtspreq == RTSPREQ_SETUP && !p_transport) { /* New Transport: setting? */ if(data->set.str[STRING_RTSP_TRANSPORT]) { - Curl_safefree(conn->allocptr.rtsp_transport); + Curl_safefree(data->state.aptr.rtsp_transport); - conn->allocptr.rtsp_transport = + data->state.aptr.rtsp_transport = aprintf("Transport: %s\r\n", data->set.str[STRING_RTSP_TRANSPORT]); - if(!conn->allocptr.rtsp_transport) + if(!data->state.aptr.rtsp_transport) return CURLE_OUT_OF_MEMORY; } else { @@ -352,7 +347,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) return CURLE_BAD_FUNCTION_ARGUMENT; } - p_transport = conn->allocptr.rtsp_transport; + p_transport = data->state.aptr.rtsp_transport; } /* Accept Headers for DESCRIBE requests */ @@ -364,14 +359,14 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* Accept-Encoding header */ if(!Curl_checkheaders(conn, "Accept-Encoding") && data->set.str[STRING_ENCODING]) { - Curl_safefree(conn->allocptr.accept_encoding); - conn->allocptr.accept_encoding = + Curl_safefree(data->state.aptr.accept_encoding); + data->state.aptr.accept_encoding = aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); - if(!conn->allocptr.accept_encoding) + if(!data->state.aptr.accept_encoding) return CURLE_OUT_OF_MEMORY; - p_accept_encoding = conn->allocptr.accept_encoding; + p_accept_encoding = data->state.aptr.accept_encoding; } } @@ -379,13 +374,13 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) 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(conn, "User-Agent") && conn->allocptr.uagent) { - Curl_safefree(conn->allocptr.uagent); - conn->allocptr.uagent = NULL; + if(Curl_checkheaders(conn, "User-Agent") && data->state.aptr.uagent) { + Curl_safefree(data->state.aptr.uagent); + data->state.aptr.uagent = NULL; } else if(!Curl_checkheaders(conn, "User-Agent") && data->set.str[STRING_USERAGENT]) { - p_uagent = conn->allocptr.uagent; + p_uagent = data->state.aptr.uagent; } /* setup the authentication headers */ @@ -393,17 +388,17 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(result) return result; - p_proxyuserpwd = conn->allocptr.proxyuserpwd; - p_userpwd = conn->allocptr.userpwd; + p_proxyuserpwd = data->state.aptr.proxyuserpwd; + p_userpwd = data->state.aptr.userpwd; /* Referrer */ - Curl_safefree(conn->allocptr.ref); + Curl_safefree(data->state.aptr.ref); if(data->change.referer && !Curl_checkheaders(conn, "Referer")) - conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer); + data->state.aptr.ref = aprintf("Referer: %s\r\n", data->change.referer); else - conn->allocptr.ref = NULL; + data->state.aptr.ref = NULL; - p_referrer = conn->allocptr.ref; + p_referrer = data->state.aptr.ref; /* * Range Header @@ -416,9 +411,9 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* Check to see if there is a range set in the custom headers */ if(!Curl_checkheaders(conn, "Range") && data->state.range) { - Curl_safefree(conn->allocptr.rangeline); - conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range); - p_range = conn->allocptr.rangeline; + Curl_safefree(data->state.aptr.rangeline); + data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range); + p_range = data->state.aptr.rangeline; } } @@ -435,16 +430,13 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } /* Initialize a dynamic send buffer */ - req_buffer = Curl_add_buffer_init(); - - if(!req_buffer) - return CURLE_OUT_OF_MEMORY; + Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER); result = - Curl_add_bufferf(&req_buffer, - "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ - "CSeq: %ld\r\n", /* CSeq */ - p_request, p_stream_uri, rtsp->CSeq_sent); + Curl_dyn_addf(&req_buffer, + "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ + "CSeq: %ld\r\n", /* CSeq */ + p_request, p_stream_uri, rtsp->CSeq_sent); if(result) return result; @@ -453,7 +445,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) * to make comparison easier */ if(p_session_id) { - result = Curl_add_bufferf(&req_buffer, "Session: %s\r\n", p_session_id); + result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id); if(result) return result; } @@ -461,42 +453,42 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* * Shared HTTP-like options */ - result = Curl_add_bufferf(&req_buffer, - "%s" /* transport */ - "%s" /* accept */ - "%s" /* accept-encoding */ - "%s" /* range */ - "%s" /* referrer */ - "%s" /* user-agent */ - "%s" /* proxyuserpwd */ - "%s" /* userpwd */ - , - p_transport ? p_transport : "", - p_accept ? p_accept : "", - p_accept_encoding ? p_accept_encoding : "", - p_range ? p_range : "", - p_referrer ? p_referrer : "", - p_uagent ? p_uagent : "", - p_proxyuserpwd ? p_proxyuserpwd : "", - p_userpwd ? p_userpwd : ""); + result = Curl_dyn_addf(&req_buffer, + "%s" /* transport */ + "%s" /* accept */ + "%s" /* accept-encoding */ + "%s" /* range */ + "%s" /* referrer */ + "%s" /* user-agent */ + "%s" /* proxyuserpwd */ + "%s" /* userpwd */ + , + p_transport ? p_transport : "", + p_accept ? p_accept : "", + p_accept_encoding ? p_accept_encoding : "", + p_range ? p_range : "", + p_referrer ? p_referrer : "", + p_uagent ? p_uagent : "", + 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(conn->allocptr.userpwd); - conn->allocptr.userpwd = NULL; + Curl_safefree(data->state.aptr.userpwd); + data->state.aptr.userpwd = NULL; if(result) return result; if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) { - result = Curl_add_timecondition(data, req_buffer); + result = Curl_add_timecondition(conn, &req_buffer); if(result) return result; } - result = Curl_add_custom_headers(conn, FALSE, req_buffer); + result = Curl_add_custom_headers(conn, FALSE, &req_buffer); if(result) return result; @@ -506,14 +498,14 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(data->set.upload) { putsize = data->state.infilesize; - data->set.httpreq = HTTPREQ_PUT; + data->state.httpreq = HTTPREQ_PUT; } else { postsize = (data->state.infilesize != -1)? data->state.infilesize: (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0); - data->set.httpreq = HTTPREQ_POST; + data->state.httpreq = HTTPREQ_POST; } if(putsize > 0 || postsize > 0) { @@ -521,9 +513,9 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) * actually set a custom Content-Length in the headers */ if(!Curl_checkheaders(conn, "Content-Length")) { result = - Curl_add_bufferf(&req_buffer, - "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", - (data->set.upload ? putsize : postsize)); + Curl_dyn_addf(&req_buffer, + "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", + (data->set.upload ? putsize : postsize)); if(result) return result; } @@ -531,8 +523,8 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(rtspreq == RTSPREQ_SET_PARAMETER || rtspreq == RTSPREQ_GET_PARAMETER) { if(!Curl_checkheaders(conn, "Content-Type")) { - result = Curl_add_bufferf(&req_buffer, - "Content-Type: text/parameters\r\n"); + result = Curl_dyn_addf(&req_buffer, + "Content-Type: text/parameters\r\n"); if(result) return result; } @@ -540,8 +532,8 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(rtspreq == RTSPREQ_ANNOUNCE) { if(!Curl_checkheaders(conn, "Content-Type")) { - result = Curl_add_bufferf(&req_buffer, - "Content-Type: application/sdp\r\n"); + result = Curl_dyn_addf(&req_buffer, + "Content-Type: application/sdp\r\n"); if(result) return result; } @@ -551,7 +543,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } else if(rtspreq == RTSPREQ_GET_PARAMETER) { /* Check for an empty GET_PARAMETER (heartbeat) request */ - data->set.httpreq = HTTPREQ_HEAD; + data->state.httpreq = HTTPREQ_HEAD; data->set.opt_no_body = TRUE; } } @@ -559,20 +551,20 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* RTSP never allows chunked transfer */ data->req.forbidchunk = TRUE; /* Finish the request buffer */ - result = Curl_add_buffer(&req_buffer, "\r\n", 2); + result = Curl_dyn_add(&req_buffer, "\r\n"); if(result) return result; if(postsize > 0) { - result = Curl_add_buffer(&req_buffer, data->set.postfields, - (size_t)postsize); + result = Curl_dyn_addn(&req_buffer, data->set.postfields, + (size_t)postsize); if(result) return result; } /* issue the request */ - result = Curl_add_buffer_send(&req_buffer, conn, - &data->info.request_size, 0, FIRSTSOCKET); + result = Curl_buffer_send(&req_buffer, conn, + &data->info.request_size, 0, FIRSTSOCKET); if(result) { failf(data, "Failed sending RTSP request"); return result; diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h index 2f9cc32c8ea..1aae86456dc 100644 --- a/Utilities/cmcurl/lib/rtsp.h +++ b/Utilities/cmcurl/lib/rtsp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/security.c b/Utilities/cmcurl/lib/security.c index 76951548d95..3b9c20a73b4 100644 --- a/Utilities/cmcurl/lib/security.c +++ b/Utilities/cmcurl/lib/security.c @@ -7,7 +7,7 @@ * rewrite to work around the paragraph 2 in the BSD licenses as explained * below. * - * Copyright (c) 1998, 1999, 2017 Kungliga Tekniska Högskolan + * Copyright (c) 1998, 1999, 2017 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * * Copyright (C) 2001 - 2019, Daniel Stenberg, , et al. @@ -191,7 +191,6 @@ static CURLcode read_data(struct connectdata *conn, struct krb5buffer *buf) { int len; - void *tmp = NULL; CURLcode result; result = socket_read(fd, &len, sizeof(len)); @@ -201,12 +200,11 @@ static CURLcode read_data(struct connectdata *conn, if(len) { /* only realloc if there was a length */ len = ntohl(len); - tmp = Curl_saferealloc(buf->data, len); + buf->data = Curl_saferealloc(buf->data, len); } - if(tmp == NULL) + if(!len || !buf->data) return CURLE_OUT_OF_MEMORY; - buf->data = tmp; result = socket_read(fd, buf->data, len); if(result) return result; @@ -238,7 +236,7 @@ static ssize_t sec_recv(struct connectdata *conn, int sockindex, /* Handle clear text response. */ if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) - return read(fd, buffer, len); + return sread(fd, buffer, len); if(conn->in_buffer.eof_flag) { conn->in_buffer.eof_flag = 0; @@ -267,7 +265,7 @@ static ssize_t sec_recv(struct connectdata *conn, int sockindex, } /* Send |length| bytes from |from| to the |fd| socket taking care of encoding - and negociating with the server. |from| can be NULL. */ + and negotiating with the server. |from| can be NULL. */ static void do_sec_send(struct connectdata *conn, curl_socket_t fd, const char *from, int length) { diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index 6e738901599..6832b428efc 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,8 +22,12 @@ #include "curl_setup.h" +#include + #ifdef HAVE_SYS_SELECT_H #include +#elif defined(HAVE_UNISTD_H) +#include #endif #if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE) @@ -48,14 +52,9 @@ #include "urldata.h" #include "connect.h" #include "select.h" +#include "timeval.h" #include "warnless.h" -/* Convenience local macros */ -#define ELAPSED_MS() (int)Curl_timediff(Curl_now(), initial_tv) - -int Curl_ack_eintr = 0; -#define ERROR_NOT_EINTR(error) (Curl_ack_eintr || error != EINTR) - /* * Internal function used for waiting a specific amount of ms * in Curl_socket_check() and Curl_poll() when no file descriptor @@ -72,15 +71,8 @@ int Curl_ack_eintr = 0; * -1 = system call error, invalid timeout value, or interrupted * 0 = specified timeout has elapsed */ -int Curl_wait_ms(int timeout_ms) +int Curl_wait_ms(timediff_t timeout_ms) { -#if !defined(MSDOS) && !defined(USE_WINSOCK) -#ifndef HAVE_POLL_FINE - struct timeval pending_tv; -#endif - struct curltime initial_tv; - int pending_ms; -#endif int r = 0; if(!timeout_ms) @@ -91,37 +83,145 @@ int Curl_wait_ms(int timeout_ms) } #if defined(MSDOS) delay(timeout_ms); -#elif defined(USE_WINSOCK) - Sleep(timeout_ms); +#elif defined(WIN32) + /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */ +#if TIMEDIFF_T_MAX >= ULONG_MAX + if(timeout_ms >= ULONG_MAX) + timeout_ms = ULONG_MAX-1; + /* don't use ULONG_MAX, because that is equal to INFINITE */ +#endif + Sleep((ULONG)timeout_ms); #else - pending_ms = timeout_ms; - initial_tv = Curl_now(); - do { - int error; #if defined(HAVE_POLL_FINE) - r = poll(NULL, 0, pending_ms); + /* prevent overflow, timeout_ms is typecast to int. */ +#if TIMEDIFF_T_MAX > INT_MAX + if(timeout_ms > INT_MAX) + timeout_ms = INT_MAX; +#endif + r = poll(NULL, 0, (int)timeout_ms); +#else + { + struct timeval pending_tv; + timediff_t tv_sec = timeout_ms / 1000; + timediff_t tv_usec = (timeout_ms % 1000) * 1000; /* max=999999 */ +#ifdef HAVE_SUSECONDS_T +#if TIMEDIFF_T_MAX > TIME_T_MAX + /* tv_sec overflow check in case time_t is signed */ + if(tv_sec > TIME_T_MAX) + tv_sec = TIME_T_MAX; +#endif + pending_tv.tv_sec = (time_t)tv_sec; + pending_tv.tv_usec = (suseconds_t)tv_usec; #else - pending_tv.tv_sec = pending_ms / 1000; - pending_tv.tv_usec = (pending_ms % 1000) * 1000; +#if TIMEDIFF_T_MAX > INT_MAX + /* tv_sec overflow check in case time_t is signed */ + if(tv_sec > INT_MAX) + tv_sec = INT_MAX; +#endif + pending_tv.tv_sec = (int)tv_sec; + pending_tv.tv_usec = (int)tv_usec; +#endif r = select(0, NULL, NULL, NULL, &pending_tv); + } #endif /* HAVE_POLL_FINE */ - if(r != -1) - break; - error = SOCKERRNO; - if(error && ERROR_NOT_EINTR(error)) - break; - pending_ms = timeout_ms - ELAPSED_MS(); - if(pending_ms <= 0) { - r = 0; /* Simulate a "call timed out" case */ - break; - } - } while(r == -1); #endif /* USE_WINSOCK */ if(r) r = -1; return r; } +/* + * This is a wrapper around select() to aid in Windows compatibility. + * A negative timeout value makes this function wait indefinitely, + * unless no valid file descriptor is given, when this happens the + * negative timeout is ignored and the function times out immediately. + * + * Return values: + * -1 = system call error or fd >= FD_SETSIZE + * 0 = timeout + * N = number of signalled file descriptors + */ +int Curl_select(curl_socket_t maxfd, /* highest socket number */ + fd_set *fds_read, /* sockets ready for reading */ + fd_set *fds_write, /* sockets ready for writing */ + fd_set *fds_err, /* sockets with errors */ + timediff_t timeout_ms) /* milliseconds to wait */ +{ + struct timeval pending_tv; + struct timeval *ptimeout; + int r; + +#ifdef USE_WINSOCK + /* WinSock select() can't handle zero events. See the comment below. */ + if((!fds_read || fds_read->fd_count == 0) && + (!fds_write || fds_write->fd_count == 0) && + (!fds_err || fds_err->fd_count == 0)) { + r = Curl_wait_ms(timeout_ms); + return r; + } +#endif + + ptimeout = &pending_tv; + if(timeout_ms < 0) { + ptimeout = NULL; + } + else if(timeout_ms > 0) { + timediff_t tv_sec = timeout_ms / 1000; + timediff_t tv_usec = (timeout_ms % 1000) * 1000; /* max=999999 */ +#ifdef HAVE_SUSECONDS_T +#if TIMEDIFF_T_MAX > TIME_T_MAX + /* tv_sec overflow check in case time_t is signed */ + if(tv_sec > TIME_T_MAX) + tv_sec = TIME_T_MAX; +#endif + pending_tv.tv_sec = (time_t)tv_sec; + pending_tv.tv_usec = (suseconds_t)tv_usec; +#elif defined(WIN32) /* maybe also others in the future */ +#if TIMEDIFF_T_MAX > LONG_MAX + /* tv_sec overflow check on Windows there we know it is long */ + if(tv_sec > LONG_MAX) + tv_sec = LONG_MAX; +#endif + pending_tv.tv_sec = (long)tv_sec; + pending_tv.tv_usec = (long)tv_usec; +#else +#if TIMEDIFF_T_MAX > INT_MAX + /* tv_sec overflow check in case time_t is signed */ + if(tv_sec > INT_MAX) + tv_sec = INT_MAX; +#endif + pending_tv.tv_sec = (int)tv_sec; + pending_tv.tv_usec = (int)tv_usec; +#endif + } + else { + pending_tv.tv_sec = 0; + pending_tv.tv_usec = 0; + } + +#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 can't 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. + + It is unclear why WinSock doesn't just handle this for us instead of + calling this an error. + */ + r = select((int)maxfd + 1, + fds_read && fds_read->fd_count ? fds_read : NULL, + fds_write && fds_write->fd_count ? fds_write : NULL, + fds_err && fds_err->fd_count ? fds_err : NULL, ptimeout); +#else + r = select((int)maxfd + 1, fds_read, fds_write, fds_err, ptimeout); +#endif + + return r; +} + /* * Wait for read or write events on a set of file descriptors. It uses poll() * when a fine poll() is available, in order to avoid limits with FD_SETSIZE, @@ -145,34 +245,24 @@ int Curl_wait_ms(int timeout_ms) int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ curl_socket_t readfd1, curl_socket_t writefd, /* socket to write to */ - time_t timeout_ms) /* milliseconds to wait */ + timediff_t timeout_ms) /* milliseconds to wait */ { #ifdef HAVE_POLL_FINE struct pollfd pfd[3]; int num; #else - struct timeval pending_tv; - struct timeval *ptimeout; fd_set fds_read; fd_set fds_write; fd_set fds_err; curl_socket_t maxfd; #endif - struct curltime initial_tv = {0, 0}; - int pending_ms = 0; int r; int ret; -#if SIZEOF_TIME_T != SIZEOF_INT - /* wrap-around precaution */ - if(timeout_ms >= INT_MAX) - timeout_ms = INT_MAX; -#endif - if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) { /* no sockets, just wait */ - r = Curl_wait_ms((int)timeout_ms); + r = Curl_wait_ms(timeout_ms); return r; } @@ -181,11 +271,6 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ when function is called with a zero timeout or a negative timeout value indicating a blocking call should be performed. */ - if(timeout_ms > 0) { - pending_ms = (int)timeout_ms; - initial_tv = Curl_now(); - } - #ifdef HAVE_POLL_FINE num = 0; @@ -208,31 +293,9 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ num++; } - do { - int error; - if(timeout_ms < 0) - pending_ms = -1; - else if(!timeout_ms) - pending_ms = 0; - r = poll(pfd, num, pending_ms); - if(r != -1) - break; - error = SOCKERRNO; - if(error && ERROR_NOT_EINTR(error)) - break; - if(timeout_ms > 0) { - pending_ms = (int)(timeout_ms - ELAPSED_MS()); - if(pending_ms <= 0) { - r = 0; /* Simulate a "call timed out" case */ - break; - } - } - } while(r == -1); - - if(r < 0) - return -1; - if(r == 0) - return 0; + r = Curl_poll(pfd, num, timeout_ms); + if(r <= 0) + return r; ret = 0; num = 0; @@ -288,62 +351,17 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ maxfd = writefd; } - ptimeout = (timeout_ms < 0) ? NULL : &pending_tv; + /* We know that we have at least one bit set in at least two fd_sets in + this case, but we may have no bits set in either fds_read or fd_write, + so check for that and handle it. Luckily, with WinSock, we can _also_ + ask how many bits are set on an fd_set. - do { - int error; - if(timeout_ms > 0) { - pending_tv.tv_sec = pending_ms / 1000; - pending_tv.tv_usec = (pending_ms % 1000) * 1000; - } - else if(!timeout_ms) { - pending_tv.tv_sec = 0; - pending_tv.tv_usec = 0; - } - - /* WinSock select() must not be called with an fd_set that contains zero - fd flags, or it will return WSAEINVAL. But, it also can't 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. - - We know that we have at least one bit set in at least two fd_sets in - this case, but we may have no bits set in either fds_read or fd_write, - so check for that and handle it. Luckily, with WinSock, we can _also_ - ask how many bits are set on an fd_set. - - It is unclear why WinSock doesn't just handle this for us instead of - calling this an error. - - Note also that WinSock ignores the first argument, so we don't worry - about the fact that maxfd is computed incorrectly with WinSock (since - curl_socket_t is unsigned in such cases and thus -1 is the largest - value). - */ -#ifdef USE_WINSOCK - r = select((int)maxfd + 1, - fds_read.fd_count ? &fds_read : NULL, - fds_write.fd_count ? &fds_write : NULL, - &fds_err, ptimeout); -#else - r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout); -#endif - - if(r != -1) - break; - error = SOCKERRNO; - if(error && ERROR_NOT_EINTR(error)) - break; - if(timeout_ms > 0) { - pending_ms = (int)(timeout_ms - ELAPSED_MS()); - if(pending_ms <= 0) { - r = 0; /* Simulate a "call timed out" case */ - break; - } - } - } while(r == -1); + Note also that WinSock ignores the first argument, so we don't worry + about the fact that maxfd is computed incorrectly with WinSock (since + curl_socket_t is unsigned in such cases and thus -1 is the largest + value). + */ + r = Curl_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms); if(r < 0) return -1; @@ -389,20 +407,18 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ * 0 = timeout * N = number of structures with non zero revent fields */ -int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms) +int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) { -#ifndef HAVE_POLL_FINE - struct timeval pending_tv; - struct timeval *ptimeout; +#ifdef HAVE_POLL_FINE + int pending_ms; +#else fd_set fds_read; fd_set fds_write; fd_set fds_err; curl_socket_t maxfd; #endif - struct curltime initial_tv = {0, 0}; bool fds_none = TRUE; unsigned int i; - int pending_ms = 0; int r; if(ufds) { @@ -414,6 +430,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms) } } if(fds_none) { + /* no sockets, just wait */ r = Curl_wait_ms(timeout_ms); return r; } @@ -423,33 +440,20 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms) when function is called with a zero timeout or a negative timeout value indicating a blocking call should be performed. */ - if(timeout_ms > 0) { - pending_ms = timeout_ms; - initial_tv = Curl_now(); - } - #ifdef HAVE_POLL_FINE - do { - int error; - if(timeout_ms < 0) - pending_ms = -1; - else if(!timeout_ms) - pending_ms = 0; - r = poll(ufds, nfds, pending_ms); - if(r != -1) - break; - error = SOCKERRNO; - if(error && ERROR_NOT_EINTR(error)) - break; - if(timeout_ms > 0) { - pending_ms = (int)(timeout_ms - ELAPSED_MS()); - if(pending_ms <= 0) { - r = 0; /* Simulate a "call timed out" case */ - break; - } - } - } while(r == -1); + /* prevent overflow, timeout_ms is typecast to int. */ +#if TIMEDIFF_T_MAX > INT_MAX + if(timeout_ms > INT_MAX) + timeout_ms = INT_MAX; +#endif + if(timeout_ms > 0) + pending_ms = (int)timeout_ms; + else if(timeout_ms < 0) + pending_ms = -1; + else + pending_ms = 0; + r = poll(ufds, nfds, pending_ms); if(r < 0) return -1; @@ -490,54 +494,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms) } } -#ifdef USE_WINSOCK - /* WinSock select() can't handle zero events. See the comment about this in - Curl_check_socket(). */ - if(fds_read.fd_count == 0 && fds_write.fd_count == 0 - && fds_err.fd_count == 0) { - r = Curl_wait_ms(timeout_ms); - return r; - } -#endif - - ptimeout = (timeout_ms < 0) ? NULL : &pending_tv; - - do { - int error; - if(timeout_ms > 0) { - pending_tv.tv_sec = pending_ms / 1000; - pending_tv.tv_usec = (pending_ms % 1000) * 1000; - } - else if(!timeout_ms) { - pending_tv.tv_sec = 0; - pending_tv.tv_usec = 0; - } - -#ifdef USE_WINSOCK - r = select((int)maxfd + 1, - /* WinSock select() can't handle fd_sets with zero bits set, so - don't give it such arguments. See the comment about this in - Curl_check_socket(). - */ - fds_read.fd_count ? &fds_read : NULL, - fds_write.fd_count ? &fds_write : NULL, - fds_err.fd_count ? &fds_err : NULL, ptimeout); -#else - r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout); -#endif - if(r != -1) - break; - error = SOCKERRNO; - if(error && ERROR_NOT_EINTR(error)) - break; - if(timeout_ms > 0) { - pending_ms = timeout_ms - ELAPSED_MS(); - if(pending_ms <= 0) { - r = 0; /* Simulate a "call timed out" case */ - break; - } - } - } while(r == -1); + r = Curl_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms); if(r < 0) return -1; diff --git a/Utilities/cmcurl/lib/select.h b/Utilities/cmcurl/lib/select.h index 9a1ba45a7d6..95181f467e0 100644 --- a/Utilities/cmcurl/lib/select.h +++ b/Utilities/cmcurl/lib/select.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -72,28 +72,26 @@ struct pollfd therefore defined here */ #define CURL_CSELECT_IN2 (CURL_CSELECT_ERR << 1) +int Curl_select(curl_socket_t maxfd, + fd_set *fds_read, + fd_set *fds_write, + fd_set *fds_err, + timediff_t timeout_ms); + int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2, curl_socket_t writefd, - time_t timeout_ms); - + timediff_t timeout_ms); #define SOCKET_READABLE(x,z) \ Curl_socket_check(x, CURL_SOCKET_BAD, CURL_SOCKET_BAD, z) #define SOCKET_WRITABLE(x,z) \ Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, x, z) -int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms); - -/* On non-DOS and non-Winsock platforms, when Curl_ack_eintr is set, - * EINTR condition is honored and function might exit early without - * awaiting full timeout. Otherwise EINTR will be ignored and full - * timeout will elapse. */ -extern int Curl_ack_eintr; - -int Curl_wait_ms(int timeout_ms); +int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms); +int Curl_wait_ms(timediff_t timeout_ms); #ifdef TPF int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes, - fd_set* excepts, struct timeval* tv); + fd_set* excepts, struct timeval *tv); #endif /* Winsock and TPF sockets are not in range [0..FD_SETSIZE-1], which @@ -109,7 +107,7 @@ int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes, SET_SOCKERRNO(EINVAL); \ return -1; \ } \ -} WHILE_FALSE +} while(0) #endif #endif /* HEADER_CURL_SELECT_H */ diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index 5913ea4060e..6943fa84e00 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -36,13 +36,14 @@ #include "sendf.h" #include "connect.h" #include "vtls/vtls.h" -#include "ssh.h" +#include "vssh/ssh.h" #include "easyif.h" #include "multiif.h" #include "non-ascii.h" #include "strerror.h" #include "select.h" #include "strdup.h" +#include "http2.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -224,7 +225,7 @@ bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex) (void)sockindex; return false; } -#define pre_receive_plain(c,n) do {} WHILE_FALSE +#define pre_receive_plain(c,n) do {} while(0) #define get_pre_recved(c,n,b,l) 0 #endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ @@ -266,7 +267,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...) size_t len; char error[CURL_ERROR_SIZE + 2]; va_start(ap, fmt); - mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); + (void)mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); len = strlen(error); if(data->set.errorbuffer && !data->state.errorbuf) { @@ -497,10 +498,12 @@ static CURLcode pausewrite(struct Curl_easy *data, is again enabled */ struct SingleRequest *k = &data->req; struct UrlState *s = &data->state; - char *dupl; unsigned int i; bool newtype = TRUE; + /* If this transfers over HTTP/2, pause the stream! */ + Curl_http2_stream_pause(data, TRUE); + if(s->tempcount) { for(i = 0; i< s->tempcount; i++) { if(s->tempwrite[i].type == type) { @@ -514,42 +517,21 @@ static CURLcode pausewrite(struct Curl_easy *data, else i = 0; - if(!newtype) { - /* append new data to old data */ - - /* figure out the new size of the data to save */ - size_t newlen = len + s->tempwrite[i].len; - /* allocate the new memory area */ - char *newptr = realloc(s->tempwrite[i].buf, newlen); - if(!newptr) - return CURLE_OUT_OF_MEMORY; - /* copy the new data to the end of the new area */ - memcpy(newptr + s->tempwrite[i].len, ptr, len); - - /* update the pointer and the size */ - s->tempwrite[i].buf = newptr; - s->tempwrite[i].len = newlen; - } - else { - dupl = Curl_memdup(ptr, len); - if(!dupl) - return CURLE_OUT_OF_MEMORY; - + if(newtype) { /* store this information in the state struct for later use */ - s->tempwrite[i].buf = dupl; - s->tempwrite[i].len = len; + Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER); s->tempwrite[i].type = type; if(newtype) s->tempcount++; } + if(Curl_dyn_addn(&s->tempwrite[i].b, (unsigned char *)ptr, len)) + return CURLE_OUT_OF_MEMORY; + /* mark the connection as RECV paused */ k->keepon |= KEEP_RECV_PAUSE; - DEBUGF(infof(data, "Paused %zu bytes in buffer for type %02x\n", - len, type)); - return CURLE_OK; } @@ -611,7 +593,7 @@ static CURLcode chop_write(struct connectdata *conn, return pausewrite(data, type, ptr, len); } if(wrote != chunklen) { - failf(data, "Failed writing body (%zu != %zu)", wrote, chunklen); + failf(data, "Failure writing output to destination"); return CURLE_WRITE_ERROR; } } @@ -692,19 +674,20 @@ CURLcode Curl_read_plain(curl_socket_t sockfd, ssize_t nread = sread(sockfd, buf, bytesfromsocket); if(-1 == nread) { - int err = SOCKERRNO; - int return_error; + const int err = SOCKERRNO; + const bool return_error = #ifdef USE_WINSOCK - return_error = WSAEWOULDBLOCK == err; + WSAEWOULDBLOCK == err #else - return_error = EWOULDBLOCK == err || EAGAIN == err || EINTR == err; + EWOULDBLOCK == err || EAGAIN == err || EINTR == err #endif + ; + *n = 0; /* no data returned */ if(return_error) return CURLE_AGAIN; return CURLE_RECV_ERROR; } - /* we only return number of bytes read when we return OK */ *n = nread; return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index 92cd5b271fe..90edf6aa7a0 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -77,6 +77,37 @@ CURLcode Curl_setstropt(char **charp, const char *s) return CURLE_OK; } +CURLcode Curl_setblobopt(struct curl_blob **blobp, + const struct curl_blob *blob) +{ + /* 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); + + if(blob) { + struct curl_blob *nblob; + if(blob->len > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + nblob = (struct curl_blob *) + malloc(sizeof(struct curl_blob) + + ((blob->flags & CURL_BLOB_COPY) ? blob->len : 0)); + if(!nblob) + return CURLE_OUT_OF_MEMORY; + *nblob = *blob; + if(blob->flags & CURL_BLOB_COPY) { + /* put the data after the blob struct in memory */ + nblob->data = (char *)nblob + sizeof(struct curl_blob); + memcpy(nblob->data, blob->data, blob->len); + } + + *blobp = nblob; + return CURLE_OK; + } + + return CURLE_OK; +} + static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) { CURLcode result = CURLE_OK; @@ -119,8 +150,11 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) #define C_SSLVERSION_VALUE(x) (x & 0xffff) #define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000) -static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, - va_list param) +/* + * Do not make Curl_vsetopt() static: it is called from + * packages/OS400/ccsidcurl.c. + */ +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) { char *argptr; CURLcode result = CURLE_OK; @@ -237,6 +271,9 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, * Do not include the body part in the output data stream. */ data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE; + if(data->set.opt_no_body) + /* in HTTP lingo, no body means using the HEAD request... */ + data->set.method = HTTPREQ_HEAD; break; case CURLOPT_FAILONERROR: /* @@ -258,13 +295,13 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, data->set.upload = (0 != va_arg(param, long)) ? TRUE : FALSE; if(data->set.upload) { /* If this is HTTP, PUT is what's needed to "upload" */ - data->set.httpreq = HTTPREQ_PUT; + data->set.method = HTTPREQ_PUT; data->set.opt_no_body = FALSE; /* this is implied */ } else /* In HTTP, the opposite of upload is GET (unless NOBODY is true as then this can be changed to HEAD later on) */ - data->set.httpreq = HTTPREQ_GET; + data->set.method = HTTPREQ_GET; break; case CURLOPT_REQUEST_TARGET: result = Curl_setstropt(&data->set.str[STRING_TARGET], @@ -312,7 +349,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, * Parse the $HOME/.netrc file */ arg = va_arg(param, long); - if((arg < CURL_NETRC_IGNORED) || (arg > CURL_NETRC_REQUIRED)) + if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.use_netrc = (enum CURL_NETRC_OPTION)arg; break; @@ -339,7 +376,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, * curl/curl.h header file. */ arg = va_arg(param, long); - if((arg < CURL_TIMECOND_NONE) || (arg > CURL_TIMECOND_LASTMOD)) + if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.timecondition = (curl_TimeCond)arg; break; @@ -360,7 +397,9 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, break; case CURLOPT_SSLVERSION: +#ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSLVERSION: +#endif /* * Set explicit SSL version to try to connect with, as some SSL * implementations are lame. @@ -368,9 +407,11 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, #ifdef USE_SSL { long version, version_max; - struct ssl_primary_config *primary = (option == CURLOPT_SSLVERSION ? - &data->set.ssl.primary : - &data->set.proxy_ssl.primary); + struct ssl_primary_config *primary = &data->set.ssl.primary; +#ifndef CURL_DISABLE_PROXY + if(option != CURLOPT_SSLVERSION) + primary = &data->set.proxy_ssl.primary; +#endif arg = va_arg(param, long); @@ -478,11 +519,11 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, CURLOPT_POSTFIELDS isn't used and the POST data is read off the callback! */ if(va_arg(param, long)) { - data->set.httpreq = HTTPREQ_POST; + data->set.method = HTTPREQ_POST; data->set.opt_no_body = FALSE; /* this is implied */ } else - data->set.httpreq = HTTPREQ_GET; + data->set.method = HTTPREQ_GET; break; case CURLOPT_COPYPOSTFIELDS: @@ -529,7 +570,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, } data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS]; - data->set.httpreq = HTTPREQ_POST; + data->set.method = HTTPREQ_POST; break; case CURLOPT_POSTFIELDS: @@ -539,7 +580,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, data->set.postfields = va_arg(param, void *); /* Release old copied data. */ (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.httpreq = HTTPREQ_POST; + data->set.method = HTTPREQ_POST; break; case CURLOPT_POSTFIELDSIZE: @@ -585,7 +626,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, * Set to make us do HTTP POST */ data->set.httppost = va_arg(param, struct curl_httppost *); - data->set.httpreq = HTTPREQ_POST_FORM; + data->set.method = HTTPREQ_POST_FORM; data->set.opt_no_body = FALSE; /* this is implied */ break; #endif /* CURL_DISABLE_HTTP */ @@ -597,7 +638,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, result = Curl_mime_set_subparts(&data->set.mimepost, va_arg(param, curl_mime *), FALSE); if(!result) { - data->set.httpreq = HTTPREQ_POST_MIME; + data->set.method = HTTPREQ_POST_MIME; data->set.opt_no_body = FALSE; /* this is implied */ } break; @@ -749,7 +790,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, } else if(strcasecompare(argptr, "FLUSH")) { /* flush cookies to file, takes care of the locking */ - Curl_flush_cookies(data, 0); + Curl_flush_cookies(data, FALSE); } else if(strcasecompare(argptr, "RELOAD")) { /* reload cookies from file */ @@ -792,7 +833,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, * Set to force us do HTTP GET */ if(va_arg(param, long)) { - data->set.httpreq = HTTPREQ_GET; + data->set.method = HTTPREQ_GET; data->set.upload = FALSE; /* switch off upload */ data->set.opt_no_body = FALSE; /* this is implied */ } @@ -806,11 +847,16 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, arg = va_arg(param, long); if(arg < CURL_HTTP_VERSION_NONE) return CURLE_BAD_FUNCTION_ARGUMENT; +#ifdef ENABLE_QUIC + if(arg == CURL_HTTP_VERSION_3) + ; + else +#endif #ifndef USE_NGHTTP2 if(arg >= CURL_HTTP_VERSION_2) return CURLE_UNSUPPORTED_PROTOCOL; #else - if(arg > CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) + if(arg >= CURL_HTTP_VERSION_LAST) return CURLE_UNSUPPORTED_PROTOCOL; if(arg == CURL_HTTP_VERSION_NONE) arg = CURL_HTTP_VERSION_2TLS; @@ -897,7 +943,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, va_arg(param, char *)); /* we don't set - data->set.httpreq = HTTPREQ_CUSTOM; + data->set.method = HTTPREQ_CUSTOM; here, we continue as if we were using the already set type and this just changes the actual request keyword */ break; @@ -1101,7 +1147,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, * How do access files over FTP. */ arg = va_arg(param, long); - if((arg < CURLFTPMETHOD_DEFAULT) || (arg > CURLFTPMETHOD_SINGLECWD)) + if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.ftp_filemethod = (curl_ftpfile)arg; break; @@ -1128,7 +1174,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, case CURLOPT_FTP_SSL_CCC: arg = va_arg(param, long); - if((arg < CURLFTPSSL_CCC_NONE) || (arg > CURLFTPSSL_CCC_ACTIVE)) + if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.ftp_ccc = (curl_ftpccc)arg; break; @@ -1156,7 +1202,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, * Set a specific auth for FTP-SSL transfers. */ arg = va_arg(param, long); - if((arg < CURLFTPAUTH_DEFAULT) || (arg > CURLFTPAUTH_TLS)) + if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.ftpsslauth = (curl_ftpauth)arg; break; @@ -1598,6 +1644,13 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, result = Curl_setstropt(&data->set.str[STRING_CERT_ORIG], va_arg(param, char *)); break; + case CURLOPT_SSLCERT_BLOB: + /* + * Blob that holds file name of the SSL certificate to use + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_ORIG], + va_arg(param, struct curl_blob *)); + break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSLCERT: /* @@ -1606,6 +1659,13 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY], va_arg(param, char *)); break; + case CURLOPT_PROXY_SSLCERT_BLOB: + /* + * Blob that holds file name of the SSL certificate to use for proxy + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_PROXY], + va_arg(param, struct curl_blob *)); + break; #endif case CURLOPT_SSLCERTTYPE: /* @@ -1630,6 +1690,13 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, result = Curl_setstropt(&data->set.str[STRING_KEY_ORIG], va_arg(param, char *)); break; + case CURLOPT_SSLKEY_BLOB: + /* + * Blob that holds file name of the SSL key to use + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_ORIG], + va_arg(param, struct curl_blob *)); + break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSLKEY: /* @@ -1638,6 +1705,13 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY], va_arg(param, char *)); break; + case CURLOPT_PROXY_SSLKEY_BLOB: + /* + * Blob that holds file name of the SSL key to use for proxy + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_PROXY], + va_arg(param, struct curl_blob *)); + break; #endif case CURLOPT_SSLKEYTYPE: /* @@ -1775,16 +1849,9 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, arg = va_arg(param, long); /* Obviously people are not reading documentation and too many thought - this argument took a boolean when it wasn't and misused it. We thus ban - 1 as a sensible input and we warn about its use. Then we only have the - 2 action internally stored as TRUE. */ - - if(1 == arg) { - failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - data->set.ssl.primary.verifyhost = (0 != arg) ? TRUE : FALSE; + this argument took a boolean when it wasn't and misused it. + Treat 1 and 2 the same */ + data->set.ssl.primary.verifyhost = (bool)((arg & 3) ? TRUE : FALSE); /* Update the current connection ssl_config. */ if(data->conn) { @@ -1799,17 +1866,8 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, */ arg = va_arg(param, long); - /* Obviously people are not reading documentation and too many thought - this argument took a boolean when it wasn't and misused it. We thus ban - 1 as a sensible input and we warn about its use. Then we only have the - 2 action internally stored as TRUE. */ - - if(1 == arg) { - failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - data->set.proxy_ssl.primary.verifyhost = (0 != arg)?TRUE:FALSE; + /* Treat both 1 and 2 as TRUE */ + data->set.proxy_ssl.primary.verifyhost = (bool)((arg & 3)?TRUE:FALSE); /* Update the current connection proxy_ssl_config. */ if(data->conn) { @@ -1978,6 +2036,30 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_ORIG], va_arg(param, char *)); break; + case CURLOPT_ISSUERCERT_BLOB: + /* + * Blob that holds Issuer certificate to check certificates issuer + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT_ORIG], + va_arg(param, struct curl_blob *)); + break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_ISSUERCERT: + /* + * Set Issuer certificate file + * to check certificates issuer + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_ISSUERCERT_BLOB: + /* + * Blob that holds Issuer certificate to check certificates issuer + */ + result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY], + va_arg(param, struct curl_blob *)); + break; +#endif #ifndef CURL_DISABLE_TELNET case CURLOPT_TELNETOPTIONS: /* @@ -2001,7 +2083,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, arg = READBUFFER_MIN; /* Resize if new size */ - if(arg != data->set.buffer_size) { + if((arg != data->set.buffer_size) && data->state.buffer) { char *newbuff = realloc(data->state.buffer, arg + 1); if(!newbuff) { DEBUGF(fprintf(stderr, "Error: realloc of buffer failed\n")); @@ -2131,7 +2213,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, * Make transfers attempt to use SSL/TLS. */ arg = va_arg(param, long); - if((arg < CURLUSESSL_NONE) || (arg > CURLUSESSL_ALL)) + if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.use_ssl = (curl_usessl)arg; break; @@ -2141,6 +2223,9 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, data->set.ssl.enable_beast = (bool)((arg&CURLSSLOPT_ALLOW_BEAST) ? TRUE : FALSE); data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); + data->set.ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); + data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); + data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); break; #ifndef CURL_DISABLE_PROXY @@ -2149,6 +2234,10 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, data->set.proxy_ssl.enable_beast = (bool)((arg&CURLSSLOPT_ALLOW_BEAST) ? TRUE : FALSE); data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); + data->set.proxy_ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); + data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); + data->set.proxy_ssl.revoke_best_effort = + !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); break; #endif @@ -2251,7 +2340,9 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, case CURLOPT_SSL_SESSIONID_CACHE: data->set.ssl.primary.sessionid = (0 != va_arg(param, long)) ? TRUE : FALSE; +#ifndef CURL_DISABLE_PROXY data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid; +#endif break; #ifdef USE_SSH @@ -2294,7 +2385,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, case CURLOPT_SSH_KEYFUNCTION: /* setting to NULL is fine since the ssh.c functions themselves will - then rever to use the internal default */ + then revert to use the internal default */ data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback); break; @@ -2371,8 +2462,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, case CURLOPT_REDIR_PROTOCOLS: /* set the bitmask for the protocols that libcurl is allowed to follow to, as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs - to be set in both bitmasks to be allowed to get redirected to. Defaults - to all protocols except FILE and SCP. */ + to be set in both bitmasks to be allowed to get redirected to. */ data->set.redir_protocols = va_arg(param, long); break; @@ -2398,8 +2488,18 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, /* Set the list of mail recipients */ data->set.mail_rcpt = va_arg(param, struct curl_slist *); break; + case CURLOPT_MAIL_RCPT_ALLLOWFAILS: + /* allow RCPT TO command to fail for some recipients */ + data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; #endif + case CURLOPT_SASL_AUTHZID: + /* Authorisation identity (identity to act as) */ + result = Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID], + va_arg(param, char *)); + break; + case CURLOPT_SASL_IR: /* Enable/disable SASL initial response */ data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE; @@ -2503,7 +2603,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, case CURLOPT_RTSP_SERVER_CSEQ: /* Same as the above, but for server-initiated requests */ - data->state.rtsp_next_client_CSeq = va_arg(param, long); + data->state.rtsp_next_server_CSeq = va_arg(param, long); break; case CURLOPT_INTERLEAVEDATA: @@ -2544,9 +2644,11 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, case CURLOPT_PROXY_TLSAUTH_USERNAME: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], va_arg(param, char *)); +#ifndef CURL_DISABLE_PROXY if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && !data->set.proxy_ssl.authtype) data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ +#endif break; case CURLOPT_TLSAUTH_PASSWORD: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_ORIG], @@ -2557,9 +2659,11 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, case CURLOPT_PROXY_TLSAUTH_PASSWORD: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], va_arg(param, char *)); +#ifndef CURL_DISABLE_PROXY if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && !data->set.proxy_ssl.authtype) data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ +#endif break; case CURLOPT_TLSAUTH_TYPE: argptr = va_arg(param, char *); @@ -2569,6 +2673,7 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, else data->set.ssl.authtype = CURL_TLSAUTH_NONE; break; +#ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_TYPE: argptr = va_arg(param, char *); if(!argptr || @@ -2578,18 +2683,35 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, data->set.proxy_ssl.authtype = CURL_TLSAUTH_NONE; break; #endif +#endif #ifdef USE_ARES case CURLOPT_DNS_SERVERS: - result = Curl_set_dns_servers(data, va_arg(param, char *)); + result = Curl_setstropt(&data->set.str[STRING_DNS_SERVERS], + va_arg(param, char *)); + if(result) + return result; + result = Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]); break; case CURLOPT_DNS_INTERFACE: - result = Curl_set_dns_interface(data, va_arg(param, char *)); + result = Curl_setstropt(&data->set.str[STRING_DNS_INTERFACE], + va_arg(param, char *)); + if(result) + return result; + result = Curl_set_dns_interface(data, data->set.str[STRING_DNS_INTERFACE]); break; case CURLOPT_DNS_LOCAL_IP4: - result = Curl_set_dns_local_ip4(data, va_arg(param, char *)); + result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP4], + va_arg(param, char *)); + if(result) + return result; + result = Curl_set_dns_local_ip4(data, data->set.str[STRING_DNS_LOCAL_IP4]); break; case CURLOPT_DNS_LOCAL_IP6: - result = Curl_set_dns_local_ip6(data, va_arg(param, char *)); + result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP6], + va_arg(param, char *)); + if(result) + return result; + result = Curl_set_dns_local_ip6(data, data->set.str[STRING_DNS_LOCAL_IP6]); break; #endif case CURLOPT_TCP_KEEPALIVE: @@ -2615,14 +2737,12 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, result = CURLE_NOT_BUILT_IN; #endif break; -#ifdef USE_NGHTTP2 case CURLOPT_SSL_ENABLE_NPN: data->set.ssl_enable_npn = (0 != va_arg(param, long)) ? TRUE : FALSE; break; case CURLOPT_SSL_ENABLE_ALPN: data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; break; -#endif #ifdef USE_UNIX_SOCKETS case CURLOPT_UNIX_SOCKET_PATH: data->set.abstract_unix_socket = FALSE; @@ -2728,7 +2848,8 @@ static CURLcode vsetopt(struct Curl_easy *data, CURLoption option, result = Curl_setstropt(&data->set.str[STRING_ALTSVC], argptr); if(result) return result; - (void)Curl_altsvc_load(data->asi, argptr); + if(argptr) + (void)Curl_altsvc_load(data->asi, argptr); break; case CURLOPT_ALTSVC_CTRL: if(!data->asi) { @@ -2770,7 +2891,7 @@ CURLcode curl_easy_setopt(struct Curl_easy *data, CURLoption tag, ...) va_start(arg, tag); - result = vsetopt(data, tag, arg); + result = Curl_vsetopt(data, tag, arg); va_end(arg); return result; diff --git a/Utilities/cmcurl/lib/setopt.h b/Utilities/cmcurl/lib/setopt.h index c658e04aeb5..5fc4368dc9e 100644 --- a/Utilities/cmcurl/lib/setopt.h +++ b/Utilities/cmcurl/lib/setopt.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,7 +23,8 @@ ***************************************************************************/ CURLcode Curl_setstropt(char **charp, const char *s); -CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, - va_list arg); +CURLcode Curl_setblobopt(struct curl_blob **blobp, + const struct curl_blob *blob); +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg); #endif /* HEADER_CURL_SETOPT_H */ diff --git a/Utilities/cmcurl/lib/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h index a3c2a7bdc99..b693cb3b37e 100644 --- a/Utilities/cmcurl/lib/setup-os400.h +++ b/Utilities/cmcurl/lib/setup-os400.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -200,17 +200,21 @@ extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status, /* Some socket functions must be wrapped to process textual addresses like AF_UNIX. */ -extern int Curl_os400_connect(int sd, struct sockaddr * destaddr, int addrlen); -extern int Curl_os400_bind(int sd, struct sockaddr * localaddr, int addrlen); +extern int Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen); +extern int Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen); extern int Curl_os400_sendto(int sd, char *buffer, int buflen, int flags, - struct sockaddr * dstaddr, int addrlen); + struct sockaddr *dstaddr, int addrlen); extern int Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags, struct sockaddr *fromaddr, int *addrlen); +extern int Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen); +extern int Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen); #define connect Curl_os400_connect #define bind Curl_os400_bind #define sendto Curl_os400_sendto #define recvfrom Curl_os400_recvfrom +#define getpeername Curl_os400_getpeername +#define getsockname Curl_os400_getsockname #ifdef HAVE_LIBZ #define zlibVersion Curl_os400_zlibVersion diff --git a/Utilities/cmcurl/lib/setup-vms.h b/Utilities/cmcurl/lib/setup-vms.h index 6c454aee682..0e39c9f6b6b 100644 --- a/Utilities/cmcurl/lib/setup-vms.h +++ b/Utilities/cmcurl/lib/setup-vms.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -73,7 +73,7 @@ char *decc$getenv(const char *__name); # endif #endif - struct passwd * decc_getpwuid(uid_t uid); + struct passwd *decc_getpwuid(uid_t uid); #ifdef __DECC # if __INITIAL_POINTER_SIZE == 32 @@ -138,9 +138,9 @@ static char *vms_getenv(const char *envvar) static struct passwd vms_passwd_cache; -static struct passwd * vms_getpwuid(uid_t uid) +static struct passwd *vms_getpwuid(uid_t uid) { - struct passwd * my_passwd; + struct passwd *my_passwd; /* Hack needed to support 64 bit builds, decc_getpwnam is 32 bit only */ #ifdef __DECC diff --git a/Utilities/cmcurl/lib/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h new file mode 100644 index 00000000000..45b58476664 --- /dev/null +++ b/Utilities/cmcurl/lib/setup-win32.h @@ -0,0 +1,123 @@ +#ifndef HEADER_CURL_SETUP_WIN32_H +#define HEADER_CURL_SETUP_WIN32_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +/* + * Include header files for windows builds before redefining anything. + * Use this preprocessor block only to include or exclude windows.h, + * winsock2.h, ws2tcpip.h or winsock.h. Any other windows thing belongs + * to any other further and independent block. Under Cygwin things work + * just as under linux (e.g. ) and the winsock headers should + * never be included when __CYGWIN__ is defined. configure script takes + * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK_H, HAVE_WINSOCK2_H, + * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined. + */ + +#ifdef HAVE_WINDOWS_H +# if defined(UNICODE) && !defined(_UNICODE) +# define _UNICODE +# endif +# if defined(_UNICODE) && !defined(UNICODE) +# define UNICODE +# endif +# include +# include +# ifdef HAVE_WINSOCK2_H +# include +# ifdef HAVE_WS2TCPIP_H +# include +# endif +# else +# ifdef HAVE_WINSOCK_H +# include +# endif +# endif +# include +# ifdef UNICODE + typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str); +# endif +#endif + +/* + * Define USE_WINSOCK to 2 if we have and use WINSOCK2 API, else + * define USE_WINSOCK to 1 if we have and use WINSOCK API, else + * undefine USE_WINSOCK. + */ + +#undef USE_WINSOCK + +#ifdef HAVE_WINSOCK2_H +# define USE_WINSOCK 2 +#else +# ifdef HAVE_WINSOCK_H +# define USE_WINSOCK 1 +# endif +#endif + +/* + * Define _WIN32_WINNT_[OS] symbols because not all Windows build systems have + * those symbols to compare against, and even those that do may be missing + * newer symbols. + */ + +#ifndef _WIN32_WINNT_NT4 +#define _WIN32_WINNT_NT4 0x0400 /* Windows NT 4.0 */ +#endif +#ifndef _WIN32_WINNT_WIN2K +#define _WIN32_WINNT_WIN2K 0x0500 /* Windows 2000 */ +#endif +#ifndef _WIN32_WINNT_WINXP +#define _WIN32_WINNT_WINXP 0x0501 /* Windows XP */ +#endif +#ifndef _WIN32_WINNT_WS03 +#define _WIN32_WINNT_WS03 0x0502 /* Windows Server 2003 */ +#endif +#ifndef _WIN32_WINNT_WIN6 +#define _WIN32_WINNT_WIN6 0x0600 /* Windows Vista */ +#endif +#ifndef _WIN32_WINNT_VISTA +#define _WIN32_WINNT_VISTA 0x0600 /* Windows Vista */ +#endif +#ifndef _WIN32_WINNT_WS08 +#define _WIN32_WINNT_WS08 0x0600 /* Windows Server 2008 */ +#endif +#ifndef _WIN32_WINNT_LONGHORN +#define _WIN32_WINNT_LONGHORN 0x0600 /* Windows Vista */ +#endif +#ifndef _WIN32_WINNT_WIN7 +#define _WIN32_WINNT_WIN7 0x0601 /* Windows 7 */ +#endif +#ifndef _WIN32_WINNT_WIN8 +#define _WIN32_WINNT_WIN8 0x0602 /* Windows 8 */ +#endif +#ifndef _WIN32_WINNT_WINBLUE +#define _WIN32_WINNT_WINBLUE 0x0603 /* Windows 8.1 */ +#endif +#ifndef _WIN32_WINNT_WINTHRESHOLD +#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 /* Windows 10 */ +#endif +#ifndef _WIN32_WINNT_WIN10 +#define _WIN32_WINNT_WIN10 0x0A00 /* Windows 10 */ +#endif + +#endif /* HEADER_CURL_SETUP_WIN32_H */ diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c index f9287af2323..ee5d273c43e 100644 --- a/Utilities/cmcurl/lib/sha256.c +++ b/Utilities/cmcurl/lib/sha256.c @@ -5,7 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Florin Petriuc, + * Copyright (C) 2017, Florin Petriuc, + * Copyright (C) 2018 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,16 +36,210 @@ #define USE_OPENSSL_SHA256 #endif +#endif /* USE_OPENSSL */ + +#ifdef USE_MBEDTLS +#include + +#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) + #define HAS_RESULT_CODE_BASED_FUNCTIONS #endif +#endif /* USE_MBEDTLS */ + +/* Please keep the SSL backend-specific #if branches in this order: + * + * 1. USE_OPENSSL + * 2. USE_GNUTLS_NETTLE + * 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. + */ + +#if defined(USE_OPENSSL_SHA256) -#ifdef USE_OPENSSL_SHA256 /* When OpenSSL is available we use the SHA256-function from OpenSSL */ #include + +#elif defined(USE_GNUTLS_NETTLE) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +typedef struct sha256_ctx SHA256_CTX; + +static void SHA256_Init(SHA256_CTX *ctx) +{ + sha256_init(ctx); +} + +static void SHA256_Update(SHA256_CTX *ctx, + const unsigned char *data, + unsigned int length) +{ + sha256_update(ctx, length, data); +} + +static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) +{ + sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); +} + +#elif defined(USE_GNUTLS) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +typedef gcry_md_hd_t SHA256_CTX; + +static void SHA256_Init(SHA256_CTX *ctx) +{ + gcry_md_open(ctx, GCRY_MD_SHA256, 0); +} + +static void SHA256_Update(SHA256_CTX *ctx, + const unsigned char *data, + unsigned int length) +{ + gcry_md_write(*ctx, data, length); +} + +static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) +{ + memcpy(digest, gcry_md_read(*ctx, 0), SHA256_DIGEST_LENGTH); + gcry_md_close(*ctx); +} + +#elif defined(USE_MBEDTLS) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +typedef mbedtls_sha256_context SHA256_CTX; + +static void SHA256_Init(SHA256_CTX *ctx) +{ +#if !defined(HAS_RESULT_CODE_BASED_FUNCTIONS) + mbedtls_sha256_starts(ctx, 0); +#else + (void) mbedtls_sha256_starts_ret(ctx, 0); +#endif +} + +static void SHA256_Update(SHA256_CTX *ctx, + const unsigned char *data, + unsigned int length) +{ +#if !defined(HAS_RESULT_CODE_BASED_FUNCTIONS) + mbedtls_sha256_update(ctx, data, length); +#else + (void) mbedtls_sha256_update_ret(ctx, data, length); +#endif +} + +static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) +{ +#if !defined(HAS_RESULT_CODE_BASED_FUNCTIONS) + mbedtls_sha256_finish(ctx, digest); +#else + (void) mbedtls_sha256_finish_ret(ctx, digest); +#endif +} + +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) + +#include + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +typedef CC_SHA256_CTX SHA256_CTX; + +static void SHA256_Init(SHA256_CTX *ctx) +{ + (void) CC_SHA256_Init(ctx); +} + +static void SHA256_Update(SHA256_CTX *ctx, + const unsigned char *data, + unsigned int length) +{ + (void) CC_SHA256_Update(ctx, data, length); +} + +static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) +{ + (void) CC_SHA256_Final(digest, ctx); +} + +#elif defined(USE_WIN32_CRYPTO) + +#include + +struct sha256_ctx { + HCRYPTPROV hCryptProv; + HCRYPTHASH hHash; +}; +typedef struct sha256_ctx SHA256_CTX; + +#if !defined(CALG_SHA_256) +#define CALG_SHA_256 0x0000800c +#endif + +static void SHA256_Init(SHA256_CTX *ctx) +{ + if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash); + } +} + +static void SHA256_Update(SHA256_CTX *ctx, + const unsigned char *data, + unsigned int length) +{ + CryptHashData(ctx->hHash, (unsigned char *) data, length, 0); +} + +static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) +{ + unsigned long length = 0; + + CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); + if(length == SHA256_DIGEST_LENGTH) + CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); + + if(ctx->hHash) + CryptDestroyHash(ctx->hHash); + + if(ctx->hCryptProv) + CryptReleaseContext(ctx->hCryptProv, 0); +} + #else /* When no other crypto library is available we use this code segment */ -/* ===== start - public domain SHA256 implementation ===== */ /* This is based on SHA256 implementation in LibTomCrypt that was released into * public domain by Tom St Denis. */ @@ -86,7 +281,7 @@ do { \ } while(0) #endif -typedef struct sha256_state { +struct sha256_state { #ifdef HAVE_LONGLONG unsigned long long length; #else @@ -94,8 +289,10 @@ typedef struct sha256_state { #endif unsigned long state[8], curlen; unsigned char buf[64]; -} SHA256_CTX; -/* the K array */ +}; +typedef struct sha256_state SHA256_CTX; + +/* The K array */ static const unsigned long K[64] = { 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, @@ -111,6 +308,7 @@ static const unsigned long K[64] = { 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL }; + /* Various logical functions */ #define RORc(x, y) \ (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | \ @@ -123,13 +321,15 @@ static const unsigned long K[64] = { #define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) #define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) #define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) -/* compress 512-bits */ + +/* Compress 512-bits */ static int sha256_compress(struct sha256_state *md, unsigned char *buf) { unsigned long S[8], W[64]; int i; - /* copy state into S */ + + /* Copy state into S */ for(i = 0; i < 8; i++) { S[i] = md->state[i]; } @@ -141,6 +341,7 @@ static int sha256_compress(struct sha256_state *md, W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; } + /* Compress */ #define RND(a,b,c,d,e,f,g,h,i) \ unsigned long t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ @@ -153,12 +354,15 @@ static int sha256_compress(struct sha256_state *md, t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; } - /* feedback */ + + /* Feedback */ for(i = 0; i < 8; i++) { md->state[i] = md->state[i] + S[i]; } + return 0; } + /* Initialize the hash state */ static void SHA256_Init(struct sha256_state *md) { @@ -173,7 +377,8 @@ static void SHA256_Init(struct sha256_state *md) md->state[6] = 0x1F83D9ABUL; md->state[7] = 0x5BE0CD19UL; } -/** + +/* Process a block of memory though the hash @param md The hash state @param in The data to hash @@ -185,6 +390,7 @@ static int SHA256_Update(struct sha256_state *md, unsigned long inlen) { unsigned long n; + #define block_size 64 if(md->curlen > sizeof(md->buf)) return -1; @@ -210,9 +416,11 @@ static int SHA256_Update(struct sha256_state *md, } } } + return 0; } -/** + +/* Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (32 bytes) @@ -222,13 +430,17 @@ static int SHA256_Final(unsigned char *out, struct sha256_state *md) { int i; + if(md->curlen >= sizeof(md->buf)) return -1; - /* increase the length of the message */ + + /* Increase the length of the message */ md->length += md->curlen * 8; - /* append the '1' bit */ + + /* Append the '1' bit */ md->buf[md->curlen++] = (unsigned char)0x80; - /* if the length is currently above 56 bytes we append zeros + + /* If the length is currently above 56 bytes we append zeros * then compress. Then we can fall back to padding zeros and length * encoding like normal. */ @@ -239,29 +451,44 @@ static int SHA256_Final(unsigned char *out, sha256_compress(md, md->buf); md->curlen = 0; } - /* pad up to 56 bytes of zeroes */ + + /* Pad up to 56 bytes of zeroes */ while(md->curlen < 56) { md->buf[md->curlen++] = (unsigned char)0; } - /* store length */ + + /* Store length */ WPA_PUT_BE64(md->buf + 56, md->length); sha256_compress(md, md->buf); - /* copy output */ + + /* Copy output */ for(i = 0; i < 8; i++) WPA_PUT_BE32(out + (4 * i), md->state[i]); + return 0; } -/* ===== end - public domain SHA256 implementation ===== */ -#endif +#endif /* CRYPTO LIBS */ -void Curl_sha256it(unsigned char *outbuffer, /* 32 unsigned chars */ - const unsigned char *input) +/* + * Curl_sha256it() + * + * Generates a SHA256 hash for the given input data. + * + * Parameters: + * + * output [in/out] - The output buffer. + * input [in] - The input data. + * length [in] - The input length. + */ +void Curl_sha256it(unsigned char *output, const unsigned char *input, + const size_t length) { SHA256_CTX ctx; + SHA256_Init(&ctx); - SHA256_Update(&ctx, input, curlx_uztoui(strlen((char *)input))); - SHA256_Final(outbuffer, &ctx); + SHA256_Update(&ctx, input, curlx_uztoui(length)); + SHA256_Final(output, &ctx); } #endif /* CURL_DISABLE_CRYPTO_AUTH */ diff --git a/Utilities/cmcurl/lib/share.c b/Utilities/cmcurl/lib/share.c index 3d5108610f1..a2d896042a1 100644 --- a/Utilities/cmcurl/lib/share.c +++ b/Utilities/cmcurl/lib/share.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -70,7 +70,7 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) case CURLSHOPT_SHARE: /* this is a type this share will share */ type = va_arg(param, int); - share->specifier |= (1<conn_cache, 103)) res = CURLSHE_NOMEM; break; @@ -116,6 +116,8 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) default: res = CURLSHE_BAD_OPTION; } + if(!res) + share->specifier |= (1<, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/slist.h b/Utilities/cmcurl/lib/slist.h index d73dbf672da..799b3c060ff 100644 --- a/Utilities/cmcurl/lib/slist.h +++ b/Utilities/cmcurl/lib/slist.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index 76c99a23015..d493adcc038 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2014, Bill Nagel , Exacq Technologies - * Copyright (C) 2016-2018, Daniel Stenberg, , et al. + * Copyright (C) 2016-2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -64,8 +64,7 @@ static CURLcode smb_request_state(struct connectdata *conn, bool *done); static CURLcode smb_done(struct connectdata *conn, CURLcode status, bool premature); static CURLcode smb_disconnect(struct connectdata *conn, bool dead); -static int smb_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); +static int smb_getsock(struct connectdata *conn, curl_socket_t *socks); static CURLcode smb_parse_url_path(struct connectdata *conn); /* @@ -607,6 +606,7 @@ static CURLcode smb_send_and_recv(struct connectdata *conn, void **msg) { struct smb_conn *smbc = &conn->proto.smbc; CURLcode result; + *msg = NULL; /* if it returns early */ /* Check if there is data in the transfer buffer */ if(!smbc->send_size && smbc->upload_size) { @@ -682,7 +682,8 @@ static CURLcode smb_connection_state(struct connectdata *conn, bool *done) switch(smbc->state) { case SMB_NEGOTIATE: - if(h->status || smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) { + if((smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) || + h->status) { connclose(conn, "SMB: negotiation failed"); return CURLE_COULDNT_CONNECT; } @@ -785,6 +786,8 @@ static CURLcode smb_request_state(struct connectdata *conn, bool *done) case SMB_OPEN: if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) { req->result = CURLE_REMOTE_FILE_NOT_FOUND; + if(h->status == smb_swap32(SMB_ERR_NOACCESS)) + req->result = CURLE_REMOTE_ACCESS_DENIED; next_state = SMB_TREE_DISCONNECT; break; } @@ -934,12 +937,8 @@ static CURLcode smb_disconnect(struct connectdata *conn, bool dead) return CURLE_OK; } -static int smb_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +static int smb_getsock(struct connectdata *conn, curl_socket_t *socks) { - if(!numsocks) - return GETSOCK_BLANK; - socks[0] = conn->sock[FIRSTSOCKET]; return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); } @@ -957,7 +956,6 @@ static CURLcode smb_do(struct connectdata *conn, bool *done) static CURLcode smb_parse_url_path(struct connectdata *conn) { - CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; struct smb_request *req = data->req.protop; struct smb_conn *smbc = &conn->proto.smbc; @@ -965,7 +963,8 @@ static CURLcode smb_parse_url_path(struct connectdata *conn) char *slash; /* URL decode the path */ - result = Curl_urldecode(data, data->state.up.path, 0, &path, NULL, TRUE); + CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &path, NULL, + REJECT_CTRL); if(result) return result; diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h index 9ce6b56157d..136a89ce9e7 100644 --- a/Utilities/cmcurl/lib/smb.h +++ b/Utilities/cmcurl/lib/smb.h @@ -8,7 +8,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2014, Bill Nagel , Exacq Technologies - * Copyright (C) 2018, Daniel Stenberg, , et al. + * Copyright (C) 2018 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -193,7 +193,6 @@ struct smb_nt_create_response { unsigned int ext_file_attributes; curl_off_t allocation_size; curl_off_t end_of_file; - } PACK; struct smb_read { diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index 4a3462b84b5..685513b3b1c 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,6 +27,9 @@ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism * RFC4954 SMTP Authentication * RFC5321 SMTP protocol + * RFC5890 Internationalized Domain Names for Applications (IDNA) + * RFC6531 SMTP Extension for Internationalized Email + * RFC6532 Internationalized Email Headers * RFC6749 OAuth 2.0 Authorization Framework * RFC8314 Use of TLS for Email Submission and Access * Draft SMTP URL Interface @@ -95,13 +98,14 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, static CURLcode smtp_connect(struct connectdata *conn, bool *done); static CURLcode smtp_disconnect(struct connectdata *conn, bool dead); static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done); -static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); +static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks); static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done); static CURLcode smtp_setup_connection(struct connectdata *conn); static CURLcode smtp_parse_url_options(struct connectdata *conn); static CURLcode smtp_parse_url_path(struct connectdata *conn); static CURLcode smtp_parse_custom_request(struct connectdata *conn); +static CURLcode smtp_parse_address(struct connectdata *conn, const char *fqma, + char **address, struct hostname *host); static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech, const char *initresp); static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp); @@ -179,7 +183,7 @@ static void smtp_to_smtps(struct connectdata *conn) conn->handler = &Curl_handler_smtps; /* Set the connection's upgraded to TLS flag */ - conn->tls_upgraded = TRUE; + conn->bits.tls_upgraded = TRUE; } #else #define smtp_to_smtps(x) Curl_nop_stmt @@ -359,10 +363,8 @@ static CURLcode smtp_perform_helo(struct connectdata *conn) */ static CURLcode smtp_perform_starttls(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* Send the STARTTLS command */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS"); + CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS"); if(!result) state(conn, SMTP_STARTTLS); @@ -378,11 +380,10 @@ static CURLcode smtp_perform_starttls(struct connectdata *conn) */ static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn) { - CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &conn->proto.smtpc; - /* Start the SSL connection */ - result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); + struct smtp_conn *smtpc = &conn->proto.smtpc; + CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, + &smtpc->ssldone); if(!result) { if(smtpc->state != SMTP_UPGRADETLS) @@ -485,13 +486,55 @@ static CURLcode smtp_perform_command(struct connectdata *conn) struct Curl_easy *data = conn->data; struct SMTP *smtp = data->req.protop; - /* Send the command */ - if(smtp->rcpt) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s", - smtp->custom && smtp->custom[0] != '\0' ? - smtp->custom : "VRFY", - smtp->rcpt->data); + if(smtp->rcpt) { + /* We notify the server we are sending UTF-8 data if a) it supports the + SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in + either the local address or host name parts. This is regardless of + whether the host name is encoded using IDN ACE */ + bool utf8 = FALSE; + + if((!smtp->custom) || (!smtp->custom[0])) { + char *address = NULL; + struct hostname host = { NULL, NULL, NULL, NULL }; + + /* Parse the mailbox to verify into the local address and host name + parts, converting the host name to an IDN A-label if necessary */ + result = smtp_parse_address(conn, smtp->rcpt->data, + &address, &host); + if(result) + return result; + + /* Establish whether we should report SMTPUTF8 to the server for this + mailbox as per RFC-6531 sect. 3.1 point 6 */ + utf8 = (conn->proto.smtpc.utf8_supported) && + ((host.encalloc) || (!Curl_is_ASCII_name(address)) || + (!Curl_is_ASCII_name(host.name))); + + /* Send the VRFY command (Note: The host name part may be absent when the + host is a local system) */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "VRFY %s%s%s%s", + address, + host.name ? "@" : "", + host.name ? host.name : "", + utf8 ? " SMTPUTF8" : ""); + + Curl_free_idnconverted_hostname(&host); + free(address); + } + else { + /* Establish whether we should report that we support SMTPUTF8 for EXPN + commands to the server as per RFC-6531 sect. 3.1 point 6 */ + utf8 = (conn->proto.smtpc.utf8_supported) && + (!strcmp(smtp->custom, "EXPN")); + + /* Send the custom recipient based command such as the EXPN command */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s%s", smtp->custom, + smtp->rcpt->data, + utf8 ? " SMTPUTF8" : ""); + } + } else + /* Send the non-recipient based command such as HELP */ result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", smtp->custom && smtp->custom[0] != '\0' ? smtp->custom : "HELP"); @@ -516,22 +559,83 @@ static CURLcode smtp_perform_mail(struct connectdata *conn) CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; + /* We notify the server we are sending UTF-8 data if a) it supports the + SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in + either the local address or host name parts. This is regardless of + whether the host name is encoded using IDN ACE */ + bool utf8 = FALSE; + /* Calculate the FROM parameter */ - if(!data->set.str[STRING_MAIL_FROM]) + if(data->set.str[STRING_MAIL_FROM]) { + char *address = NULL; + struct hostname host = { NULL, NULL, NULL, NULL }; + + /* Parse the FROM mailbox into the local address and host name parts, + converting the host name to an IDN A-label if necessary */ + result = smtp_parse_address(conn, data->set.str[STRING_MAIL_FROM], + &address, &host); + if(result) + return result; + + /* Establish whether we should report SMTPUTF8 to the server for this + mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ + utf8 = (conn->proto.smtpc.utf8_supported) && + ((host.encalloc) || (!Curl_is_ASCII_name(address)) || + (!Curl_is_ASCII_name(host.name))); + + if(host.name) { + from = aprintf("<%s@%s>", address, host.name); + + Curl_free_idnconverted_hostname(&host); + } + else + /* An invalid mailbox was provided but we'll simply let the server worry + about that and reply with a 501 error */ + from = aprintf("<%s>", address); + + free(address); + } + else /* Null reverse-path, RFC-5321, sect. 3.6.3 */ from = strdup("<>"); - else if(data->set.str[STRING_MAIL_FROM][0] == '<') - from = aprintf("%s", data->set.str[STRING_MAIL_FROM]); - else - from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]); if(!from) return CURLE_OUT_OF_MEMORY; /* Calculate the optional AUTH parameter */ if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) { - if(data->set.str[STRING_MAIL_AUTH][0] != '\0') - auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]); + if(data->set.str[STRING_MAIL_AUTH][0] != '\0') { + char *address = NULL; + struct hostname host = { NULL, NULL, NULL, NULL }; + + /* Parse the AUTH mailbox into the local address and host name parts, + converting the host name to an IDN A-label if necessary */ + result = smtp_parse_address(conn, data->set.str[STRING_MAIL_AUTH], + &address, &host); + if(result) { + free(from); + return result; + } + + /* Establish whether we should report SMTPUTF8 to the server for this + mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ + if((!utf8) && (conn->proto.smtpc.utf8_supported) && + ((host.encalloc) || (!Curl_is_ASCII_name(address)) || + (!Curl_is_ASCII_name(host.name)))) + utf8 = TRUE; + + if(host.name) { + auth = aprintf("<%s@%s>", address, host.name); + + Curl_free_idnconverted_hostname(&host); + } + else + /* An invalid mailbox was provided but we'll simply let the server + worry about it */ + auth = aprintf("<%s>", address); + + free(address); + } else /* Empty AUTH, RFC-2554, sect. 5 */ auth = strdup("<>"); @@ -565,6 +669,7 @@ static CURLcode smtp_perform_mail(struct connectdata *conn) if(result) { free(from); free(auth); + return result; } @@ -587,19 +692,33 @@ static CURLcode smtp_perform_mail(struct connectdata *conn) } } + /* If the mailboxes in the FROM and AUTH parameters don't include a UTF-8 + based address then quickly scan through the recipient list and check if + any there do, as we need to correctly identify our support for SMTPUTF8 + in the envelope, as per RFC-6531 sect. 3.4 */ + if(conn->proto.smtpc.utf8_supported && !utf8) { + struct SMTP *smtp = data->req.protop; + struct curl_slist *rcpt = smtp->rcpt; + + while(rcpt && !utf8) { + /* Does the host name contain non-ASCII characters? */ + if(!Curl_is_ASCII_name(rcpt->data)) + utf8 = TRUE; + + rcpt = rcpt->next; + } + } + /* Send the MAIL command */ - if(!auth && !size) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s", from); - else if(auth && !size) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s AUTH=%s", from, auth); - else if(auth && size) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size); - else - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s SIZE=%s", from, size); + result = Curl_pp_sendf(&conn->proto.smtpc.pp, + "MAIL FROM:%s%s%s%s%s%s", + from, /* Mandatory */ + auth ? " AUTH=" : "", /* Optional on AUTH support */ + auth ? auth : "", /* */ + size ? " SIZE=" : "", /* Optional on SIZE support */ + size ? size : "", /* */ + utf8 ? " SMTPUTF8" /* Internationalised mailbox */ + : ""); /* included in our envelope */ free(from); free(auth); @@ -623,14 +742,28 @@ static CURLcode smtp_perform_rcpt_to(struct connectdata *conn) CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; struct SMTP *smtp = data->req.protop; + char *address = NULL; + struct hostname host = { NULL, NULL, NULL, NULL }; + + /* Parse the recipient mailbox into the local address and host name parts, + converting the host name to an IDN A-label if necessary */ + result = smtp_parse_address(conn, smtp->rcpt->data, + &address, &host); + if(result) + return result; /* Send the RCPT TO command */ - if(smtp->rcpt->data[0] == '<') - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s", - smtp->rcpt->data); + if(host.name) + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s@%s>", address, + host.name); else - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", - smtp->rcpt->data); + /* An invalid mailbox was provided but we'll simply let the server worry + about that and reply with a 501 error */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", address); + + Curl_free_idnconverted_hostname(&host); + free(address); + if(!result) state(conn, SMTP_RCPT); @@ -645,10 +778,8 @@ static CURLcode smtp_perform_rcpt_to(struct connectdata *conn) */ static CURLcode smtp_perform_quit(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* Send the QUIT command */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT"); + CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT"); if(!result) state(conn, SMTP_QUIT); @@ -720,7 +851,7 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, result = CURLE_REMOTE_ACCESS_DENIED; } } - else { + else if(len >= 4) { line += 4; len -= 4; @@ -732,6 +863,10 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, else if(len >= 4 && !memcmp(line, "SIZE", 4)) smtpc->size_supported = TRUE; + /* Does the server support the UTF-8 capability? */ + else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8)) + smtpc->utf8_supported = TRUE; + /* Does the server support authentication? */ else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { smtpc->auth_supported = TRUE; @@ -791,6 +926,10 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, result = smtp_perform_authentication(conn); } } + else { + failf(data, "Unexpectedly short EHLO response"); + result = CURLE_WEIRD_SERVER_REPLY; + } return result; } @@ -917,25 +1056,53 @@ static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; struct SMTP *smtp = data->req.protop; + bool is_smtp_err = FALSE; + bool is_smtp_blocking_err = FALSE; (void)instate; /* no use for this yet */ - if(smtpcode/100 != 2) { - failf(data, "RCPT failed: %d", smtpcode); - result = CURLE_SEND_ERROR; + is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE; + + /* If there's multiple RCPT TO to be issued, it's possible to ignore errors + and proceed with only the valid addresses. */ + is_smtp_blocking_err = + (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE; + + if(is_smtp_err) { + /* Remembering the last failure which we can report if all "RCPT TO" have + failed and we cannot proceed. */ + smtp->rcpt_last_error = smtpcode; + + if(is_smtp_blocking_err) { + failf(data, "RCPT failed: %d", smtpcode); + result = CURLE_SEND_ERROR; + } } else { + /* Some RCPT TO commands have succeeded. */ + smtp->rcpt_had_ok = TRUE; + } + + if(!is_smtp_blocking_err) { smtp->rcpt = smtp->rcpt->next; if(smtp->rcpt) /* Send the next RCPT TO command */ result = smtp_perform_rcpt_to(conn); else { - /* Send the DATA command */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA"); + /* We weren't able to issue a successful RCPT TO command while going + over recipients (potentially multiple). Sending back last error. */ + if(!smtp->rcpt_had_ok) { + failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error); + result = CURLE_SEND_ERROR; + } + else { + /* Send the DATA command */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA"); - if(!result) - state(conn, SMTP_DATA); + if(!result) + state(conn, SMTP_DATA); + } } } @@ -1119,10 +1286,9 @@ static CURLcode smtp_init(struct connectdata *conn) } /* For the SMTP "protocol connect" and "doing" phases only */ -static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks) { - return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks); + return Curl_pp_getsock(&conn->proto.smtpc.pp, socks); } /*********************************************************************** @@ -1290,6 +1456,12 @@ static CURLcode smtp_perform(struct connectdata *conn, bool *connected, /* Store the first recipient (or NULL if not specified) */ smtp->rcpt = data->set.mail_rcpt; + /* Track of whether we've successfully sent at least one RCPT TO command */ + smtp->rcpt_had_ok = FALSE; + + /* Track of the last error we've received by sending RCPT TO command */ + smtp->rcpt_last_error = 0; + /* Initial data character is the first character in line: it is implicitly preceded by a virtual CRLF. */ smtp->trailing_crlf = TRUE; @@ -1445,7 +1617,7 @@ static CURLcode smtp_setup_connection(struct connectdata *conn) CURLcode result; /* Clear the TLS upgraded flag */ - conn->tls_upgraded = FALSE; + conn->bits.tls_upgraded = FALSE; /* Initialise the SMTP layer */ result = smtp_init(conn); @@ -1517,7 +1689,8 @@ static CURLcode smtp_parse_url_path(struct connectdata *conn) } /* URL decode the path and use it as the domain in our EHLO */ - return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE); + return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, + REJECT_CTRL); } /*********************************************************************** @@ -1535,7 +1708,77 @@ static CURLcode smtp_parse_custom_request(struct connectdata *conn) /* URL decode the custom request */ if(custom) - result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE); + result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, REJECT_CTRL); + + return result; +} + +/*********************************************************************** + * + * smtp_parse_address() + * + * Parse the fully qualified mailbox address into a local address part and the + * host name, converting the host name to an IDN A-label, as per RFC-5890, if + * necessary. + * + * Parameters: + * + * conn [in] - The connection handle. + * fqma [in] - The fully qualified mailbox address (which may or + * may not contain UTF-8 characters). + * address [in/out] - A new allocated buffer which holds the local + * address part of the mailbox. This buffer must be + * free'ed by the caller. + * host [in/out] - The host name structure that holds the original, + * and optionally encoded, host name. + * Curl_free_idnconverted_hostname() must be called + * once the caller has finished with the structure. + * + * Returns CURLE_OK on success. + * + * Notes: + * + * Should a UTF-8 host name require conversion to IDN ACE and we cannot honor + * that conversion then we shall return success. This allow the caller to send + * the data to the server as a U-label (as per RFC-6531 sect. 3.2). + * + * If an mailbox '@' separator cannot be located then the mailbox is considered + * to be either a local mailbox or an invalid mailbox (depending on what the + * calling function deems it to be) then the input will simply be returned in + * the address part with the host name being NULL. + */ +static CURLcode smtp_parse_address(struct connectdata *conn, const char *fqma, + char **address, struct hostname *host) +{ + CURLcode result = CURLE_OK; + size_t length; + + /* Duplicate the fully qualified email address so we can manipulate it, + ensuring it doesn't contain the delimiters if specified */ + char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma); + if(!dup) + return CURLE_OUT_OF_MEMORY; + + length = strlen(dup); + if(dup[length - 1] == '>') + dup[length - 1] = '\0'; + + /* Extract the host name from the address (if we can) */ + host->name = strpbrk(dup, "@"); + if(host->name) { + *host->name = '\0'; + host->name = host->name + 1; + + /* Attempt to convert the host name to IDN ACE */ + (void) Curl_idnconvert_hostname(conn, host); + + /* If Curl_idnconvert_hostname() fails then we shall attempt to continue + and send the host name using UTF-8 rather than as 7-bit ACE (which is + our preference) */ + } + + /* Extract the local address from the mailbox */ + *address = dup; return result; } diff --git a/Utilities/cmcurl/lib/smtp.h b/Utilities/cmcurl/lib/smtp.h index b67340a40cc..164a175d759 100644 --- a/Utilities/cmcurl/lib/smtp.h +++ b/Utilities/cmcurl/lib/smtp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2009 - 2014, Daniel Stenberg, , et al. + * Copyright (C) 2009 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -55,6 +55,9 @@ struct SMTP { curl_pp_transfer transfer; char *custom; /* Custom Request */ struct curl_slist *rcpt; /* Recipient list */ + bool rcpt_had_ok; /* Whether any of RCPT TO commands (depends on + total number of recipients) succeeded so far */ + int rcpt_last_error; /* The last error received for RCPT TO command */ size_t eob; /* Number of bytes of the EOB (End Of Body) that have been received so far */ bool trailing_crlf; /* Specifies if the tailing CRLF is present */ @@ -71,6 +74,8 @@ struct smtp_conn { bool tls_supported; /* StartTLS capability supported by server */ bool size_supported; /* If server supports SIZE extension according to RFC 1870 */ + bool utf8_supported; /* If server supports SMTPUTF8 extension according + to RFC 6531 */ bool auth_supported; /* AUTH capability supported by server */ }; diff --git a/Utilities/cmcurl/lib/sockaddr.h b/Utilities/cmcurl/lib/sockaddr.h index db146803ab9..b037ee06c2e 100644 --- a/Utilities/cmcurl/lib/sockaddr.h +++ b/Utilities/cmcurl/lib/sockaddr.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c new file mode 100644 index 00000000000..1ec0d75a468 --- /dev/null +++ b/Utilities/cmcurl/lib/socketpair.c @@ -0,0 +1,121 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2019, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "socketpair.h" + +#ifndef HAVE_SOCKETPAIR +#ifdef WIN32 +/* + * This is a socketpair() implementation for Windows. + */ +#include +#include +#include +#include +#include +#else +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include /* IPPROTO_TCP */ +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001 +#endif /* !INADDR_LOOPBACK */ +#endif /* !WIN32 */ + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +int Curl_socketpair(int domain, int type, int protocol, + curl_socket_t socks[2]) +{ + union { + struct sockaddr_in inaddr; + struct sockaddr addr; + } a; + curl_socket_t listener; + curl_socklen_t addrlen = sizeof(a.inaddr); + int reuse = 1; + char data[2][12]; + ssize_t dlen; + (void)domain; + (void)type; + (void)protocol; + + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(listener == CURL_SOCKET_BAD) + return -1; + + memset(&a, 0, sizeof(a)); + a.inaddr.sin_family = AF_INET; + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_port = 0; + + socks[0] = socks[1] = CURL_SOCKET_BAD; + + if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, + (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1) + goto error; + if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1) + goto error; + if(getsockname(listener, &a.addr, &addrlen) == -1) + goto error; + if(listen(listener, 1) == -1) + goto error; + socks[0] = socket(AF_INET, SOCK_STREAM, 0); + if(socks[0] == CURL_SOCKET_BAD) + goto error; + if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1) + goto error; + socks[1] = accept(listener, NULL, NULL); + if(socks[1] == CURL_SOCKET_BAD) + goto error; + + /* verify that nothing else connected */ + msnprintf(data[0], sizeof(data[0]), "%p", socks); + dlen = strlen(data[0]); + if(swrite(socks[0], data[0], dlen) != dlen) + goto error; + if(sread(socks[1], data[1], sizeof(data[1])) != dlen) + goto error; + if(memcmp(data[0], data[1], dlen)) + goto error; + + sclose(listener); + return 0; + + error: + sclose(listener); + sclose(socks[0]); + sclose(socks[1]); + return -1; +} + +#endif /* ! HAVE_SOCKETPAIR */ diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h new file mode 100644 index 00000000000..be9fb24f97e --- /dev/null +++ b/Utilities/cmcurl/lib/socketpair.h @@ -0,0 +1,36 @@ +#ifndef HEADER_CURL_SOCKETPAIR_H +#define HEADER_CURL_SOCKETPAIR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2019, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" +#ifndef HAVE_SOCKETPAIR +int Curl_socketpair(int domain, int type, int protocol, + curl_socket_t socks[2]); +#else +#define Curl_socketpair(a,b,c,d) socketpair(a,b,c,d) +#endif + +/* Defined here to allow specific build configs to disable it completely */ +#define USE_SOCKETPAIR 1 + +#endif /* HEADER_CURL_SOCKETPAIR_H */ diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index d8fcc3bbba0..b2215fef30c 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -37,16 +37,19 @@ #include "connect.h" #include "timeval.h" #include "socks.h" +#include "multiif.h" /* for getsock macros */ -/* The last #include file should be: */ +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" #include "memdebug.h" +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* * Helper read-from-socket functions. Does the same as Curl_read() but it * blocks until all bytes amount of buffersize will be read. No more, no less. * - * This is STUPID BLOCKING behaviour which we frown upon, but right now this - * is what we have... + * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions. */ int Curl_blockread_all(struct connectdata *conn, /* connection data */ curl_socket_t sockfd, /* read from this socket */ @@ -59,13 +62,15 @@ int Curl_blockread_all(struct connectdata *conn, /* connection data */ int result; *n = 0; for(;;) { - timediff_t timeleft = Curl_timeleft(conn->data, NULL, TRUE); - if(timeleft < 0) { + timediff_t timeout_ms = Curl_timeleft(conn->data, NULL, TRUE); + if(timeout_ms < 0) { /* we already got the timeout */ result = CURLE_OPERATION_TIMEDOUT; break; } - if(SOCKET_READABLE(sockfd, timeleft) <= 0) { + if(!timeout_ms) + timeout_ms = TIMEDIFF_T_MAX; + if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) { result = ~CURLE_OK; break; } @@ -92,6 +97,81 @@ int Curl_blockread_all(struct connectdata *conn, /* connection data */ } return result; } +#endif + +#ifndef DEBUGBUILD +#define sxstate(x,y) socksstate(x,y) +#else +#define sxstate(x,y) socksstate(x,y, __LINE__) +#endif + + +/* always use this function to change state, to make debugging easier */ +static void socksstate(struct connectdata *conn, + enum connect_t state +#ifdef DEBUGBUILD + , int lineno +#endif +) +{ + enum connect_t oldstate = conn->cnnct.state; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* synced with the state list in urldata.h */ + static const char * const statename[] = { + "INIT", + "SOCKS_INIT", + "SOCKS_SEND", + "SOCKS_READ_INIT", + "SOCKS_READ", + "GSSAPI_INIT", + "AUTH_INIT", + "AUTH_SEND", + "AUTH_READ", + "REQ_INIT", + "RESOLVING", + "RESOLVED", + "RESOLVE_REMOTE", + "REQ_SEND", + "REQ_SENDING", + "REQ_READ", + "REQ_READ_MORE", + "DONE" + }; +#endif + + if(oldstate == state) + /* don't bother when the new state is the same as the old state */ + return; + + conn->cnnct.state = state; + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + infof(conn->data, + "SXSTATE: %s => %s conn %p; line %d\n", + statename[oldstate], statename[conn->cnnct.state], conn, + lineno); +#endif +} + +int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock, + int sockindex) +{ + int rc = 0; + sock[0] = conn->sock[sockindex]; + switch(conn->cnnct.state) { + case CONNECT_RESOLVING: + case CONNECT_SOCKS_READ: + case CONNECT_AUTH_READ: + case CONNECT_REQ_READ: + case CONNECT_REQ_READ_MORE: + rc = GETSOCK_READSOCK(0); + break; + default: + rc = GETSOCK_WRITESOCK(0); + break; + } + return rc; +} /* * This function logs in to a SOCKS4 proxy and sends the specifics to the final @@ -108,62 +188,91 @@ CURLcode Curl_SOCKS4(const char *proxy_user, const char *hostname, int remote_port, int sockindex, - struct connectdata *conn) + struct connectdata *conn, + bool *done) { const bool protocol4a = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; -#define SOCKS4REQLEN 262 - unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user - id */ - CURLcode code; - curl_socket_t sock = conn->sock[sockindex]; + unsigned char *socksreq = &conn->cnnct.socksreq[0]; + CURLcode result; + curl_socket_t sockfd = conn->sock[sockindex]; struct Curl_easy *data = conn->data; + struct connstate *sx = &conn->cnnct; + struct Curl_dns_entry *dns = NULL; + ssize_t actualread; + ssize_t written; - if(Curl_timeleft(data, NULL, TRUE) < 0) { - /* time-out, bail out, go home */ - failf(data, "Connection time-out"); - return CURLE_OPERATION_TIMEDOUT; - } - - if(conn->bits.httpproxy) - infof(conn->data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n", - protocol4a ? "a" : "", hostname, remote_port); - - (void)curlx_nonblock(sock, FALSE); - - infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port); + if(!SOCKS_STATE(sx->state) && !*done) + sxstate(conn, CONNECT_SOCKS_INIT); - /* - * Compose socks4 request - * - * Request format - * - * +----+----+----+----+----+----+----+----+----+----+....+----+ - * | VN | CD | DSTPORT | DSTIP | USERID |NULL| - * +----+----+----+----+----+----+----+----+----+----+....+----+ - * # of bytes: 1 1 2 4 variable 1 - */ + switch(sx->state) { + case CONNECT_SOCKS_INIT: + /* SOCKS4 can only do IPv4, insist! */ + conn->ip_version = CURL_IPRESOLVE_V4; + if(conn->bits.httpproxy) + infof(conn->data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n", + protocol4a ? "a" : "", hostname, remote_port); - socksreq[0] = 4; /* version (SOCKS4) */ - socksreq[1] = 1; /* connect */ - socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */ - socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */ + infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port); - /* DNS resolve only for SOCKS4, not SOCKS4a */ - if(!protocol4a) { - struct Curl_dns_entry *dns; - Curl_addrinfo *hp = NULL; - int rc; + /* + * Compose socks4 request + * + * Request format + * + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * | VN | CD | DSTPORT | DSTIP | USERID |NULL| + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * # of bytes: 1 1 2 4 variable 1 + */ - rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns); + socksreq[0] = 4; /* version (SOCKS4) */ + socksreq[1] = 1; /* connect */ + socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */ + socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */ + + /* DNS resolve only for SOCKS4, not SOCKS4a */ + if(!protocol4a) { + enum resolve_t rc = + Curl_resolv(conn, hostname, remote_port, FALSE, &dns); + + if(rc == CURLRESOLV_ERROR) + return CURLE_COULDNT_RESOLVE_PROXY; + else if(rc == CURLRESOLV_PENDING) { + sxstate(conn, CONNECT_RESOLVING); + infof(data, "SOCKS4 non-blocking resolve of %s\n", hostname); + return CURLE_OK; + } + sxstate(conn, CONNECT_RESOLVED); + goto CONNECT_RESOLVED; + } - if(rc == CURLRESOLV_ERROR) - return CURLE_COULDNT_RESOLVE_PROXY; + /* socks4a doesn't resolve anything locally */ + sxstate(conn, CONNECT_REQ_INIT); + goto CONNECT_REQ_INIT; - if(rc == CURLRESOLV_PENDING) - /* ignores the return code, but 'dns' remains NULL on failure */ - (void)Curl_resolver_wait_resolv(conn, &dns); + case CONNECT_RESOLVING: + /* check if we have the name resolved by now */ + dns = Curl_fetch_addr(conn, hostname, (int)conn->port); + if(dns) { +#ifdef CURLRES_ASYNCH + conn->async.dns = dns; + conn->async.done = TRUE; +#endif + infof(data, "Hostname '%s' was found\n", hostname); + sxstate(conn, CONNECT_RESOLVED); + } + else { + result = Curl_resolv_check(data->conn, &dns); + if(!dns) + return result; + } + /* FALLTHROUGH */ + CONNECT_RESOLVED: + case CONNECT_RESOLVED: { + struct Curl_addrinfo *hp = NULL; + char buf[64]; /* * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. @@ -171,7 +280,6 @@ CURLcode Curl_SOCKS4(const char *proxy_user, if(dns) hp = dns->addr; if(hp) { - char buf[64]; Curl_printable_address(hp, buf, sizeof(buf)); if(hp->ai_family == AF_INET) { @@ -187,7 +295,6 @@ CURLcode Curl_SOCKS4(const char *proxy_user, } else { hp = NULL; /* fail! */ - failf(data, "SOCKS4 connection to %s not supported\n", buf); } @@ -199,149 +306,171 @@ CURLcode Curl_SOCKS4(const char *proxy_user, return CURLE_COULDNT_RESOLVE_HOST; } } - - /* - * This is currently not supporting "Identification Protocol (RFC1413)". - */ - socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ - if(proxy_user) { - size_t plen = strlen(proxy_user); - if(plen >= sizeof(socksreq) - 8) { - failf(data, "Too long SOCKS proxy name, can't use!\n"); - return CURLE_COULDNT_CONNECT; + /* FALLTHROUGH */ + CONNECT_REQ_INIT: + case CONNECT_REQ_INIT: + /* + * This is currently not supporting "Identification Protocol (RFC1413)". + */ + socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ + if(proxy_user) { + size_t plen = strlen(proxy_user); + if(plen >= sizeof(sx->socksreq) - 8) { + failf(data, "Too long SOCKS proxy name, can't use!\n"); + return CURLE_COULDNT_CONNECT; + } + /* copy the proxy name WITH trailing zero */ + memcpy(socksreq + 8, proxy_user, plen + 1); } - /* copy the proxy name WITH trailing zero */ - memcpy(socksreq + 8, proxy_user, plen + 1); - } - /* - * Make connection - */ - { - int result; - ssize_t actualread; - ssize_t written; - ssize_t hostnamelen = 0; - ssize_t packetsize = 9 + - strlen((char *)socksreq + 8); /* size including NUL */ - - /* If SOCKS4a, set special invalid IP address 0.0.0.x */ - if(protocol4a) { - socksreq[4] = 0; - socksreq[5] = 0; - socksreq[6] = 0; - socksreq[7] = 1; - /* If still enough room in buffer, also append hostname */ - hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */ - if(packetsize + hostnamelen <= SOCKS4REQLEN) - strcpy((char *)socksreq + packetsize, hostname); - else - hostnamelen = 0; /* Flag: hostname did not fit in buffer */ + /* + * Make connection + */ + { + ssize_t packetsize = 9 + + strlen((char *)socksreq + 8); /* size including NUL */ + + /* If SOCKS4a, set special invalid IP address 0.0.0.x */ + if(protocol4a) { + ssize_t hostnamelen = 0; + socksreq[4] = 0; + socksreq[5] = 0; + socksreq[6] = 0; + socksreq[7] = 1; + /* append hostname */ + hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */ + if(hostnamelen <= 255) + strcpy((char *)socksreq + packetsize, hostname); + else { + failf(data, "SOCKS4: too long host name"); + return CURLE_COULDNT_CONNECT; + } + packetsize += hostnamelen; + } + sx->outp = socksreq; + sx->outstanding = packetsize; + sxstate(conn, CONNECT_REQ_SENDING); } - + /* FALLTHROUGH */ + case CONNECT_REQ_SENDING: /* Send request */ - code = Curl_write_plain(conn, sock, (char *)socksreq, - packetsize + hostnamelen, - &written); - if(code || (written != packetsize + hostnamelen)) { + result = Curl_write_plain(conn, sockfd, (char *)sx->outp, + sx->outstanding, &written); + if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to send SOCKS4 connect request."); return CURLE_COULDNT_CONNECT; } - if(protocol4a && hostnamelen == 0) { - /* SOCKS4a with very long hostname - send that name separately */ - hostnamelen = (ssize_t)strlen(hostname) + 1; - code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen, - &written); - if(code || (written != hostnamelen)) { - failf(data, "Failed to send SOCKS4 connect request."); - return CURLE_COULDNT_CONNECT; - } + if(written != sx->outstanding) { + /* not done, remain in state */ + sx->outstanding -= written; + sx->outp += written; + return CURLE_OK; } - packetsize = 8; /* receive data size */ + /* done sending! */ + sx->outstanding = 8; /* receive data size */ + sx->outp = socksreq; + sxstate(conn, CONNECT_SOCKS_READ); + /* FALLTHROUGH */ + case CONNECT_SOCKS_READ: /* Receive response */ - result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize, - &actualread); - if(result || (actualread != packetsize)) { - failf(data, "Failed to receive SOCKS4 connect request ack."); + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { + failf(data, "SOCKS4: Failed receiving connect request ack: %s", + curl_easy_strerror(result)); return CURLE_COULDNT_CONNECT; } - - /* - * Response format - * - * +----+----+----+----+----+----+----+----+ - * | VN | CD | DSTPORT | DSTIP | - * +----+----+----+----+----+----+----+----+ - * # of bytes: 1 1 2 4 - * - * VN is the version of the reply code and should be 0. CD is the result - * code with one of the following values: - * - * 90: request granted - * 91: request rejected or failed - * 92: request rejected because SOCKS server cannot connect to - * identd on the client - * 93: request rejected because the client program and identd - * report different user-ids - */ - - /* wrong version ? */ - if(socksreq[0] != 0) { - failf(data, - "SOCKS4 reply has wrong version, version should be 0."); + else if(!result && !actualread) { + /* connection closed */ + failf(data, "connection to proxy closed"); return CURLE_COULDNT_CONNECT; } - - /* Result */ - switch(socksreq[1]) { - case 90: - infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":""); - break; - case 91: - failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" - ", request rejected or failed.", - (unsigned char)socksreq[4], (unsigned char)socksreq[5], - (unsigned char)socksreq[6], (unsigned char)socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); - return CURLE_COULDNT_CONNECT; - case 92: - failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" - ", request rejected because SOCKS server cannot connect to " - "identd on the client.", - (unsigned char)socksreq[4], (unsigned char)socksreq[5], - (unsigned char)socksreq[6], (unsigned char)socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); - return CURLE_COULDNT_CONNECT; - case 93: - failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" - ", request rejected because the client program and identd " - "report different user-ids.", - (unsigned char)socksreq[4], (unsigned char)socksreq[5], - (unsigned char)socksreq[6], (unsigned char)socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); - return CURLE_COULDNT_CONNECT; - default: - failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" - ", Unknown.", - (unsigned char)socksreq[4], (unsigned char)socksreq[5], - (unsigned char)socksreq[6], (unsigned char)socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); - return CURLE_COULDNT_CONNECT; + else if(actualread != sx->outstanding) { + /* remain in reading state */ + sx->outstanding -= actualread; + sx->outp += actualread; + return CURLE_OK; } + sxstate(conn, CONNECT_DONE); + break; + default: /* lots of unused states in SOCKS4 */ + break; + } + + /* + * Response format + * + * +----+----+----+----+----+----+----+----+ + * | VN | CD | DSTPORT | DSTIP | + * +----+----+----+----+----+----+----+----+ + * # of bytes: 1 1 2 4 + * + * VN is the version of the reply code and should be 0. CD is the result + * code with one of the following values: + * + * 90: request granted + * 91: request rejected or failed + * 92: request rejected because SOCKS server cannot connect to + * identd on the client + * 93: request rejected because the client program and identd + * report different user-ids + */ + + /* wrong version ? */ + if(socksreq[0] != 0) { + failf(data, + "SOCKS4 reply has wrong version, version should be 0."); + return CURLE_COULDNT_CONNECT; } - (void)curlx_nonblock(sock, TRUE); + /* Result */ + switch(socksreq[1]) { + case 90: + infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":""); + break; + case 91: + failf(data, + "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", request rejected or failed.", + (unsigned char)socksreq[4], (unsigned char)socksreq[5], + (unsigned char)socksreq[6], (unsigned char)socksreq[7], + (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), + (unsigned char)socksreq[1]); + return CURLE_COULDNT_CONNECT; + case 92: + failf(data, + "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", request rejected because SOCKS server cannot connect to " + "identd on the client.", + (unsigned char)socksreq[4], (unsigned char)socksreq[5], + (unsigned char)socksreq[6], (unsigned char)socksreq[7], + (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), + (unsigned char)socksreq[1]); + return CURLE_COULDNT_CONNECT; + case 93: + failf(data, + "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", request rejected because the client program and identd " + "report different user-ids.", + (unsigned char)socksreq[4], (unsigned char)socksreq[5], + (unsigned char)socksreq[6], (unsigned char)socksreq[7], + (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), + (unsigned char)socksreq[1]); + return CURLE_COULDNT_CONNECT; + default: + failf(data, + "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", Unknown.", + (unsigned char)socksreq[4], (unsigned char)socksreq[5], + (unsigned char)socksreq[6], (unsigned char)socksreq[7], + (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), + (unsigned char)socksreq[1]); + return CURLE_COULDNT_CONNECT; + } + *done = TRUE; return CURLE_OK; /* Proxy was successful! */ } @@ -354,7 +483,8 @@ CURLcode Curl_SOCKS5(const char *proxy_user, const char *hostname, int remote_port, int sockindex, - struct connectdata *conn) + struct connectdata *conn, + bool *done) { /* According to the RFC1928, section "6. Replies". This is what a SOCK5 @@ -372,140 +502,162 @@ CURLcode Curl_SOCKS5(const char *proxy_user, o REP Reply field: o X'00' succeeded */ - - unsigned char socksreq[600]; /* room for large user/pw (255 max each) */ + unsigned char *socksreq = &conn->cnnct.socksreq[0]; + char dest[256] = "unknown"; /* printable hostname:port */ int idx; ssize_t actualread; ssize_t written; - int result; - CURLcode code; - curl_socket_t sock = conn->sock[sockindex]; + CURLcode result; + curl_socket_t sockfd = conn->sock[sockindex]; struct Curl_easy *data = conn->data; - timediff_t timeout; bool socks5_resolve_local = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; const size_t hostname_len = strlen(hostname); ssize_t len = 0; const unsigned long auth = data->set.socks5auth; bool allow_gssapi = FALSE; + struct connstate *sx = &conn->cnnct; + struct Curl_dns_entry *dns = NULL; + + if(!SOCKS_STATE(sx->state) && !*done) + sxstate(conn, CONNECT_SOCKS_INIT); + + switch(sx->state) { + case CONNECT_SOCKS_INIT: + if(conn->bits.httpproxy) + infof(conn->data, "SOCKS5: connecting to HTTP proxy %s port %d\n", + hostname, remote_port); + + /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ + if(!socks5_resolve_local && hostname_len > 255) { + infof(conn->data, "SOCKS5: server resolving disabled for hostnames of " + "length > 255 [actual len=%zu]\n", hostname_len); + socks5_resolve_local = TRUE; + } - if(conn->bits.httpproxy) - infof(conn->data, "SOCKS5: connecting to HTTP proxy %s port %d\n", - hostname, remote_port); - - /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ - if(!socks5_resolve_local && hostname_len > 255) { - infof(conn->data, "SOCKS5: server resolving disabled for hostnames of " - "length > 255 [actual len=%zu]\n", hostname_len); - socks5_resolve_local = TRUE; - } - - /* get timeout */ - timeout = Curl_timeleft(data, NULL, TRUE); - - if(timeout < 0) { - /* time-out, bail out, go home */ - failf(data, "Connection time-out"); - return CURLE_OPERATION_TIMEDOUT; - } - - (void)curlx_nonblock(sock, TRUE); - - /* wait until socket gets connected */ - result = SOCKET_WRITABLE(sock, timeout); - - if(-1 == result) { - failf(conn->data, "SOCKS5: no connection here"); - return CURLE_COULDNT_CONNECT; - } - if(0 == result) { - failf(conn->data, "SOCKS5: connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - if(result & CURL_CSELECT_ERR) { - failf(conn->data, "SOCKS5: error occurred during connection"); - return CURLE_COULDNT_CONNECT; - } - - if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) - infof(conn->data, - "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n", - auth); - if(!(auth & CURLAUTH_BASIC)) - /* disable username/password auth */ - proxy_user = NULL; + if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) + infof(conn->data, + "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n", + auth); + if(!(auth & CURLAUTH_BASIC)) + /* disable username/password auth */ + proxy_user = NULL; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(auth & CURLAUTH_GSSAPI) - allow_gssapi = TRUE; + if(auth & CURLAUTH_GSSAPI) + allow_gssapi = TRUE; #endif - idx = 0; - socksreq[idx++] = 5; /* version */ - idx++; /* reserve for the number of authentication methods */ - socksreq[idx++] = 0; /* no authentication */ - if(allow_gssapi) - socksreq[idx++] = 1; /* GSS-API */ - if(proxy_user) - socksreq[idx++] = 2; /* username/password */ - /* write the number of authentication methods */ - socksreq[1] = (unsigned char) (idx - 2); - - (void)curlx_nonblock(sock, FALSE); - - infof(data, "SOCKS5 communication to %s:%d\n", hostname, remote_port); - - code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), - &written); - if(code || (written != (2 + (int)socksreq[1]))) { - failf(data, "Unable to send initial SOCKS5 request."); - return CURLE_COULDNT_CONNECT; - } - - (void)curlx_nonblock(sock, TRUE); - - result = SOCKET_READABLE(sock, timeout); - - if(-1 == result) { - failf(conn->data, "SOCKS5 nothing to read"); - return CURLE_COULDNT_CONNECT; - } - if(0 == result) { - failf(conn->data, "SOCKS5 read timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - if(result & CURL_CSELECT_ERR) { - failf(conn->data, "SOCKS5 read error occurred"); - return CURLE_RECV_ERROR; - } - - (void)curlx_nonblock(sock, FALSE); - - result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread); - if(result || (actualread != 2)) { - failf(data, "Unable to receive initial SOCKS5 response."); - return CURLE_COULDNT_CONNECT; - } - - if(socksreq[0] != 5) { - failf(data, "Received invalid version in initial SOCKS5 response."); - return CURLE_COULDNT_CONNECT; - } - if(socksreq[1] == 0) { - /* Nothing to do, no authentication needed */ - ; - } -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - else if(allow_gssapi && (socksreq[1] == 1)) { - code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn); - if(code) { - failf(data, "Unable to negotiate SOCKS5 GSS-API context."); + idx = 0; + socksreq[idx++] = 5; /* version */ + idx++; /* number of authentication methods */ + socksreq[idx++] = 0; /* no authentication */ + if(allow_gssapi) + socksreq[idx++] = 1; /* GSS-API */ + if(proxy_user) + socksreq[idx++] = 2; /* username/password */ + /* write the number of authentication methods */ + socksreq[1] = (unsigned char) (idx - 2); + + result = Curl_write_plain(conn, sockfd, (char *)socksreq, idx, &written); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Unable to send initial SOCKS5 request."); return CURLE_COULDNT_CONNECT; } - } + if(written != idx) { + sxstate(conn, CONNECT_SOCKS_SEND); + sx->outstanding = idx - written; + sx->outp = &socksreq[written]; + return CURLE_OK; + } + sxstate(conn, CONNECT_SOCKS_READ); + goto CONNECT_SOCKS_READ_INIT; + case CONNECT_SOCKS_SEND: + result = Curl_write_plain(conn, sockfd, (char *)sx->outp, + sx->outstanding, &written); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Unable to send initial SOCKS5 request."); + return CURLE_COULDNT_CONNECT; + } + if(written != sx->outstanding) { + /* not done, remain in state */ + sx->outstanding -= written; + sx->outp += written; + return CURLE_OK; + } + /* FALLTHROUGH */ + CONNECT_SOCKS_READ_INIT: + case CONNECT_SOCKS_READ_INIT: + sx->outstanding = 2; /* expect two bytes */ + sx->outp = socksreq; /* store it here */ + /* FALLTHROUGH */ + case CONNECT_SOCKS_READ: + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Unable to receive initial SOCKS5 response."); + return CURLE_COULDNT_CONNECT; + } + else if(!result && !actualread) { + /* connection closed */ + failf(data, "Connection to proxy closed"); + return CURLE_COULDNT_CONNECT; + } + else if(actualread != sx->outstanding) { + /* remain in reading state */ + sx->outstanding -= actualread; + sx->outp += actualread; + return CURLE_OK; + } + else if(socksreq[0] != 5) { + failf(data, "Received invalid version in initial SOCKS5 response."); + return CURLE_COULDNT_CONNECT; + } + else if(socksreq[1] == 0) { + /* DONE! No authentication needed. Send request. */ + sxstate(conn, CONNECT_REQ_INIT); + goto CONNECT_REQ_INIT; + } + else if(socksreq[1] == 2) { + /* regular name + password authentication */ + sxstate(conn, CONNECT_AUTH_INIT); + goto CONNECT_AUTH_INIT; + } +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + else if(allow_gssapi && (socksreq[1] == 1)) { + sxstate(conn, CONNECT_GSSAPI_INIT); + result = Curl_SOCKS5_gssapi_negotiate(sockindex, conn); + if(result) { + failf(data, "Unable to negotiate SOCKS5 GSS-API context."); + return CURLE_COULDNT_CONNECT; + } + } +#endif + else { + /* error */ + if(!allow_gssapi && (socksreq[1] == 1)) { + failf(data, + "SOCKS5 GSSAPI per-message authentication is not supported."); + return CURLE_COULDNT_CONNECT; + } + else if(socksreq[1] == 255) { + failf(data, "No authentication method was acceptable."); + return CURLE_COULDNT_CONNECT; + } + } + failf(data, + "Undocumented SOCKS5 mode attempted to be used by server."); + return CURLE_COULDNT_CONNECT; +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + case CONNECT_GSSAPI_INIT: + /* GSSAPI stuff done non-blocking */ + break; #endif - else if(socksreq[1] == 2) { + + default: /* do nothing! */ + break; + + CONNECT_AUTH_INIT: + case CONNECT_AUTH_INIT: { /* Needs user name and password */ size_t proxy_user_len, proxy_password_len; if(proxy_user && proxy_password) { @@ -546,257 +698,310 @@ CURLcode Curl_SOCKS5(const char *proxy_user, memcpy(socksreq + len, proxy_password, proxy_password_len); } len += proxy_password_len; - - code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written); - if(code || (len != written)) { + sxstate(conn, CONNECT_AUTH_SEND); + sx->outstanding = len; + sx->outp = socksreq; + } + /* FALLTHROUGH */ + case CONNECT_AUTH_SEND: + result = Curl_write_plain(conn, sockfd, (char *)sx->outp, + sx->outstanding, &written); + if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to send SOCKS5 sub-negotiation request."); return CURLE_COULDNT_CONNECT; } - - result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread); - if(result || (actualread != 2)) { + if(sx->outstanding != written) { + /* remain in state */ + sx->outstanding -= written; + sx->outp += written; + return CURLE_OK; + } + sx->outp = socksreq; + sx->outstanding = 2; + sxstate(conn, CONNECT_AUTH_READ); + /* FALLTHROUGH */ + case CONNECT_AUTH_READ: + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { failf(data, "Unable to receive SOCKS5 sub-negotiation response."); return CURLE_COULDNT_CONNECT; } - + else if(!result && !actualread) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLE_COULDNT_CONNECT; + } + else if(actualread != sx->outstanding) { + /* remain in state */ + sx->outstanding -= actualread; + sx->outp += actualread; + return CURLE_OK; + } /* ignore the first (VER) byte */ - if(socksreq[1] != 0) { /* status */ + else if(socksreq[1] != 0) { /* status */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); return CURLE_COULDNT_CONNECT; } /* Everything is good so far, user was authenticated! */ - } - else { - /* error */ - if(!allow_gssapi && (socksreq[1] == 1)) { - failf(data, - "SOCKS5 GSSAPI per-message authentication is not supported."); - return CURLE_COULDNT_CONNECT; - } - if(socksreq[1] == 255) { - if(!proxy_user || !*proxy_user) { - failf(data, - "No authentication method was acceptable. (It is quite likely" - " that the SOCKS5 server wanted a username/password, since none" - " was supplied to the server on this connection.)"); + sxstate(conn, CONNECT_REQ_INIT); + /* FALLTHROUGH */ + CONNECT_REQ_INIT: + case CONNECT_REQ_INIT: + if(socks5_resolve_local) { + enum resolve_t rc = Curl_resolv(conn, hostname, remote_port, + FALSE, &dns); + + if(rc == CURLRESOLV_ERROR) + return CURLE_COULDNT_RESOLVE_HOST; + + if(rc == CURLRESOLV_PENDING) { + sxstate(conn, CONNECT_RESOLVING); + return CURLE_OK; } - else { - failf(data, "No authentication method was acceptable."); - } - return CURLE_COULDNT_CONNECT; + sxstate(conn, CONNECT_RESOLVED); + goto CONNECT_RESOLVED; } - else { - failf(data, - "Undocumented SOCKS5 mode attempted to be used by server."); - return CURLE_COULDNT_CONNECT; - } - } - - /* Authentication is complete, now specify destination to the proxy */ - len = 0; - socksreq[len++] = 5; /* version (SOCKS5) */ - socksreq[len++] = 1; /* connect */ - socksreq[len++] = 0; /* must be zero */ - - if(!socks5_resolve_local) { - socksreq[len++] = 3; /* ATYP: domain name = 3 */ - socksreq[len++] = (char) hostname_len; /* address length */ - memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */ - len += hostname_len; - } - else { - struct Curl_dns_entry *dns; - Curl_addrinfo *hp = NULL; - int rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns); + goto CONNECT_RESOLVE_REMOTE; - if(rc == CURLRESOLV_ERROR) - return CURLE_COULDNT_RESOLVE_HOST; + case CONNECT_RESOLVING: + /* check if we have the name resolved by now */ + dns = Curl_fetch_addr(conn, hostname, (int)conn->port); - if(rc == CURLRESOLV_PENDING) { - /* this requires that we're in "wait for resolve" state */ - code = Curl_resolver_wait_resolv(conn, &dns); - if(code) - return code; + if(dns) { +#ifdef CURLRES_ASYNCH + conn->async.dns = dns; + conn->async.done = TRUE; +#endif + infof(data, "SOCKS5: hostname '%s' found\n", hostname); } - /* - * We cannot use 'hostent' as a struct that Curl_resolv() returns. It - * returns a Curl_addrinfo pointer that may not always look the same. - */ + if(!dns) { + result = Curl_resolv_check(data->conn, &dns); + if(!dns) + return result; + } + /* FALLTHROUGH */ + CONNECT_RESOLVED: + case CONNECT_RESOLVED: { + struct Curl_addrinfo *hp = NULL; + size_t destlen; if(dns) hp = dns->addr; - if(hp) { - char buf[64]; - Curl_printable_address(hp, buf, sizeof(buf)); - - if(hp->ai_family == AF_INET) { - int i; - struct sockaddr_in *saddr_in; - socksreq[len++] = 1; /* ATYP: IPv4 = 1 */ - - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; - for(i = 0; i < 4; i++) { - socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i]; - } + if(!hp) { + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", + hostname); + return CURLE_COULDNT_RESOLVE_HOST; + } - infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", buf); - } -#ifdef ENABLE_IPV6 - else if(hp->ai_family == AF_INET6) { - int i; - struct sockaddr_in6 *saddr_in6; - socksreq[len++] = 4; /* ATYP: IPv6 = 4 */ - - saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; - for(i = 0; i < 16; i++) { - socksreq[len++] = - ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i]; - } + Curl_printable_address(hp, dest, sizeof(dest)); + destlen = strlen(dest); + msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port); - infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", buf); + len = 0; + socksreq[len++] = 5; /* version (SOCKS5) */ + socksreq[len++] = 1; /* connect */ + socksreq[len++] = 0; /* must be zero */ + if(hp->ai_family == AF_INET) { + int i; + struct sockaddr_in *saddr_in; + socksreq[len++] = 1; /* ATYP: IPv4 = 1 */ + + saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; + for(i = 0; i < 4; i++) { + socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i]; } -#endif - else { - hp = NULL; /* fail! */ - failf(data, "SOCKS5 connection to %s not supported\n", buf); + infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", dest); + } +#ifdef ENABLE_IPV6 + else if(hp->ai_family == AF_INET6) { + int i; + struct sockaddr_in6 *saddr_in6; + socksreq[len++] = 4; /* ATYP: IPv6 = 4 */ + + saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; + for(i = 0; i < 16; i++) { + socksreq[len++] = + ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i]; } - Curl_resolv_unlock(data, dns); /* not used anymore from now on */ + infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", dest); } - if(!hp) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", - hostname); - return CURLE_COULDNT_RESOLVE_HOST; +#endif + else { + hp = NULL; /* fail! */ + failf(data, "SOCKS5 connection to %s not supported\n", dest); } + + Curl_resolv_unlock(data, dns); /* not used anymore from now on */ + goto CONNECT_REQ_SEND; } + CONNECT_RESOLVE_REMOTE: + case CONNECT_RESOLVE_REMOTE: + /* Authentication is complete, now specify destination to the proxy */ + len = 0; + socksreq[len++] = 5; /* version (SOCKS5) */ + socksreq[len++] = 1; /* connect */ + socksreq[len++] = 0; /* must be zero */ + + if(!socks5_resolve_local) { + socksreq[len++] = 3; /* ATYP: domain name = 3 */ + socksreq[len++] = (char) hostname_len; /* one byte address length */ + memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */ + len += hostname_len; + infof(data, "SOCKS5 connect to %s:%d (remotely resolved)\n", + hostname, remote_port); + } + /* FALLTHROUGH */ - socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */ - socksreq[len++] = (unsigned char)(remote_port & 0xff); /* PORT LSB */ + CONNECT_REQ_SEND: + case CONNECT_REQ_SEND: + /* PORT MSB */ + socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); + /* PORT LSB */ + socksreq[len++] = (unsigned char)(remote_port & 0xff); #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(conn->socks5_gssapi_enctype) { - failf(data, "SOCKS5 GSS-API protection not yet implemented."); - } - else + if(conn->socks5_gssapi_enctype) { + failf(data, "SOCKS5 GSS-API protection not yet implemented."); + return CURLE_COULDNT_CONNECT; + } #endif - code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written); - - if(code || (len != written)) { - failf(data, "Failed to send SOCKS5 connect request."); - return CURLE_COULDNT_CONNECT; - } - - len = 10; /* minimum packet size is 10 */ - + sx->outp = socksreq; + sx->outstanding = len; + sxstate(conn, CONNECT_REQ_SENDING); + /* FALLTHROUGH */ + case CONNECT_REQ_SENDING: + result = Curl_write_plain(conn, sockfd, (char *)sx->outp, + sx->outstanding, &written); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Failed to send SOCKS5 connect request."); + return CURLE_COULDNT_CONNECT; + } + if(sx->outstanding != written) { + /* remain in state */ + sx->outstanding -= written; + sx->outp += written; + return CURLE_OK; + } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(conn->socks5_gssapi_enctype) { - failf(data, "SOCKS5 GSS-API protection not yet implemented."); - } - else + if(conn->socks5_gssapi_enctype) { + failf(data, "SOCKS5 GSS-API protection not yet implemented."); + return CURLE_COULDNT_CONNECT; + } #endif - result = Curl_blockread_all(conn, sock, (char *)socksreq, - len, &actualread); - - if(result || (len != actualread)) { - failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLE_COULDNT_CONNECT; - } - - if(socksreq[0] != 5) { /* version */ - failf(data, - "SOCKS5 reply has wrong version, version should be 5."); - return CURLE_COULDNT_CONNECT; - } - - /* Fix: in general, returned BND.ADDR is variable length parameter by RFC - 1928, so the reply packet should be read until the end to avoid errors at - subsequent protocol level. - - +----+-----+-------+------+----------+----------+ - |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - +----+-----+-------+------+----------+----------+ - | 1 | 1 | X'00' | 1 | Variable | 2 | - +----+-----+-------+------+----------+----------+ + sx->outstanding = 10; /* minimum packet size is 10 */ + sx->outp = socksreq; + sxstate(conn, CONNECT_REQ_READ); + /* FALLTHROUGH */ + case CONNECT_REQ_READ: + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Failed to receive SOCKS5 connect request ack."); + return CURLE_COULDNT_CONNECT; + } + else if(!result && !actualread) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLE_COULDNT_CONNECT; + } + else if(actualread != sx->outstanding) { + /* remain in state */ + sx->outstanding -= actualread; + sx->outp += actualread; + return CURLE_OK; + } - ATYP: - o IP v4 address: X'01', BND.ADDR = 4 byte - o domain name: X'03', BND.ADDR = [ 1 byte length, string ] - o IP v6 address: X'04', BND.ADDR = 16 byte - */ + if(socksreq[0] != 5) { /* version */ + failf(data, + "SOCKS5 reply has wrong version, version should be 5."); + return CURLE_COULDNT_CONNECT; + } + else if(socksreq[1] != 0) { /* Anything besides 0 is an error */ + failf(data, "Can't complete SOCKS5 connection to %s. (%d)", + hostname, (unsigned char)socksreq[1]); + return CURLE_COULDNT_CONNECT; + } - /* Calculate real packet size */ - if(socksreq[3] == 3) { - /* domain name */ - int addrlen = (int) socksreq[4]; - len = 5 + addrlen + 2; - } - else if(socksreq[3] == 4) { - /* IPv6 */ - len = 4 + 16 + 2; - } + /* Fix: in general, returned BND.ADDR is variable length parameter by RFC + 1928, so the reply packet should be read until the end to avoid errors + at subsequent protocol level. + + +----+-----+-------+------+----------+----------+ + |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + ATYP: + o IP v4 address: X'01', BND.ADDR = 4 byte + o domain name: X'03', BND.ADDR = [ 1 byte length, string ] + o IP v6 address: X'04', BND.ADDR = 16 byte + */ + + /* Calculate real packet size */ + if(socksreq[3] == 3) { + /* domain name */ + int addrlen = (int) socksreq[4]; + len = 5 + addrlen + 2; + } + else if(socksreq[3] == 4) { + /* IPv6 */ + len = 4 + 16 + 2; + } + else if(socksreq[3] == 1) { + len = 4 + 4 + 2; + } + else { + failf(data, "SOCKS5 reply has wrong address type."); + return CURLE_COULDNT_CONNECT; + } - /* At this point we already read first 10 bytes */ + /* At this point we already read first 10 bytes */ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(!conn->socks5_gssapi_enctype) { - /* decrypt_gssapi_blockread already read the whole packet */ + if(!conn->socks5_gssapi_enctype) { + /* decrypt_gssapi_blockread already read the whole packet */ #endif - if(len > 10) { - result = Curl_blockread_all(conn, sock, (char *)&socksreq[10], - len - 10, &actualread); - if(result || ((len - 10) != actualread)) { - failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLE_COULDNT_CONNECT; + if(len > 10) { + sx->outstanding = len - 10; /* get the rest */ + sx->outp = &socksreq[10]; + sxstate(conn, CONNECT_REQ_READ_MORE); + } + else { + sxstate(conn, CONNECT_DONE); + break; } - } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - } + } #endif - - if(socksreq[1] != 0) { /* Anything besides 0 is an error */ - if(socksreq[3] == 1) { - failf(data, - "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)", - (unsigned char)socksreq[4], (unsigned char)socksreq[5], - (unsigned char)socksreq[6], (unsigned char)socksreq[7], - (((unsigned char)socksreq[8] << 8) | - (unsigned char)socksreq[9]), - (unsigned char)socksreq[1]); - } - else if(socksreq[3] == 3) { - unsigned char port_upper = (unsigned char)socksreq[len - 2]; - socksreq[len - 2] = 0; - failf(data, - "Can't complete SOCKS5 connection to %s:%d. (%d)", - (char *)&socksreq[5], - ((port_upper << 8) | - (unsigned char)socksreq[len - 1]), - (unsigned char)socksreq[1]); - socksreq[len - 2] = port_upper; + /* FALLTHROUGH */ + case CONNECT_REQ_READ_MORE: + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Failed to receive SOCKS5 connect request ack."); + return CURLE_COULDNT_CONNECT; } - else if(socksreq[3] == 4) { - failf(data, - "Can't complete SOCKS5 connection to %02x%02x:%02x%02x:" - "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%d. (%d)", - (unsigned char)socksreq[4], (unsigned char)socksreq[5], - (unsigned char)socksreq[6], (unsigned char)socksreq[7], - (unsigned char)socksreq[8], (unsigned char)socksreq[9], - (unsigned char)socksreq[10], (unsigned char)socksreq[11], - (unsigned char)socksreq[12], (unsigned char)socksreq[13], - (unsigned char)socksreq[14], (unsigned char)socksreq[15], - (unsigned char)socksreq[16], (unsigned char)socksreq[17], - (unsigned char)socksreq[18], (unsigned char)socksreq[19], - (((unsigned char)socksreq[20] << 8) | - (unsigned char)socksreq[21]), - (unsigned char)socksreq[1]); + else if(!result && !actualread) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLE_COULDNT_CONNECT; } - return CURLE_COULDNT_CONNECT; + else if(actualread != sx->outstanding) { + /* remain in state */ + sx->outstanding -= actualread; + sx->outp += actualread; + return CURLE_OK; + } + sxstate(conn, CONNECT_DONE); } infof(data, "SOCKS5 request granted.\n"); - (void)curlx_nonblock(sock, TRUE); + *done = TRUE; return CURLE_OK; /* Proxy was successful! */ } diff --git a/Utilities/cmcurl/lib/socks.h b/Utilities/cmcurl/lib/socks.h index daa07c12750..64a7563373c 100644 --- a/Utilities/cmcurl/lib/socks.h +++ b/Utilities/cmcurl/lib/socks.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,13 +27,13 @@ #ifdef CURL_DISABLE_PROXY #define Curl_SOCKS4(a,b,c,d,e) CURLE_NOT_BUILT_IN #define Curl_SOCKS5(a,b,c,d,e,f) CURLE_NOT_BUILT_IN +#define Curl_SOCKS_getsock(x,y,z) 0 #else /* * Helper read-from-socket functions. Does the same as Curl_read() but it * blocks until all bytes amount of buffersize will be read. No more, no less. * - * This is STUPID BLOCKING behaviour which we frown upon, but right now this - * is what we have... + * This is STUPID BLOCKING behavior */ int Curl_blockread_all(struct connectdata *conn, curl_socket_t sockfd, @@ -41,6 +41,9 @@ int Curl_blockread_all(struct connectdata *conn, ssize_t buffersize, ssize_t *n); +int Curl_SOCKS_getsock(struct connectdata *conn, + curl_socket_t *sock, + int sockindex); /* * This function logs in to a SOCKS4(a) proxy and sends the specifics to the * final destination server. @@ -49,7 +52,8 @@ CURLcode Curl_SOCKS4(const char *proxy_name, const char *hostname, int remote_port, int sockindex, - struct connectdata *conn); + struct connectdata *conn, + bool *done); /* * This function logs in to a SOCKS5 proxy and sends the specifics to the @@ -60,7 +64,8 @@ CURLcode Curl_SOCKS5(const char *proxy_name, const char *hostname, int remote_port, int sockindex, - struct connectdata *conn); + struct connectdata *conn, + bool *done); #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c index 65294bbebd1..2e36b9940af 100644 --- a/Utilities/cmcurl/lib/socks_gssapi.c +++ b/Utilities/cmcurl/lib/socks_gssapi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * + * Copyright (C) 2012 - 2020, Daniel Stenberg, , et al. * Copyright (C) 2009, Markus Moeller, - * Copyright (C) 2012 - 2018, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -115,7 +115,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc* gss_token = GSS_C_NO_BUFFER; + gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; gss_name_t server = GSS_C_NO_NAME; gss_name_t gss_client_name = GSS_C_NO_NAME; unsigned short us_length; @@ -167,6 +167,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } + (void)curlx_nonblock(sock, FALSE); + /* As long as we need to keep sending some context info, and there's no */ /* errors, keep sending it... */ for(;;) { @@ -225,7 +227,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_recv_token); - if(gss_major_status != GSS_S_CONTINUE_NEEDED) break; + if(gss_major_status != GSS_S_CONTINUE_NEEDED) + break; /* analyse response */ @@ -325,7 +328,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, user[gss_send_token.length] = '\0'; gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); - infof(data, "SOCKS5 server authencticated user %s with GSS-API.\n",user); + infof(data, "SOCKS5 server authenticated user %s with GSS-API.\n",user); free(user); user = NULL; @@ -513,6 +516,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, gss_release_buffer(&gss_status, &gss_recv_token); } + (void)curlx_nonblock(sock, TRUE); + infof(data, "SOCKS5 access with%s protection granted.\n", (socksreq[0] == 0)?"out GSS-API data": ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality")); diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c index 57027ef686e..2f1fd36fa4a 100644 --- a/Utilities/cmcurl/lib/socks_sspi.c +++ b/Utilities/cmcurl/lib/socks_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2020, Daniel Stenberg, , et al. * Copyright (C) 2009, 2011, Markus Moeller, * * This software is licensed as described in the file COPYING, which @@ -153,12 +153,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } + (void)curlx_nonblock(sock, FALSE); + /* As long as we need to keep sending some context info, and there's no */ /* errors, keep sending it... */ for(;;) { TCHAR *sname; - sname = Curl_convert_UTF8_to_tchar(service_name); + sname = curlx_convert_UTF8_to_tchar(service_name); if(!sname) return CURLE_OUT_OF_MEMORY; @@ -178,7 +180,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, &sspi_ret_flags, &expiry); - Curl_unicodefree(sname); + curlx_unicodefree(sname); if(sspi_recv_token.pvBuffer) { s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); @@ -325,7 +327,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, failf(data, "Failed to determine user name."); return CURLE_COULDNT_CONNECT; } - infof(data, "SOCKS5 server authencticated user %s with GSS-API.\n", + infof(data, "SOCKS5 server authenticated user %s with GSS-API.\n", names.sUserName); s_pSecFn->FreeContextBuffer(names.sUserName); @@ -587,6 +589,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); } + (void)curlx_nonblock(sock, TRUE); infof(data, "SOCKS5 access with%s protection granted.\n", (socksreq[0] == 0)?"out GSS-API data": diff --git a/Utilities/cmcurl/lib/splay.h b/Utilities/cmcurl/lib/splay.h index 4612ec271f0..9292f349b57 100644 --- a/Utilities/cmcurl/lib/splay.h +++ b/Utilities/cmcurl/lib/splay.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1997 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1997 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -59,10 +59,4 @@ int Curl_splayremovebyaddr(struct Curl_tree *t, ( ((i.tv_usec) < (j.tv_usec)) ? -1 : \ ( ((i.tv_usec) > (j.tv_usec)) ? 1 : 0)))) -#ifdef DEBUGBUILD -void Curl_splayprint(struct Curl_tree * t, int d, char output); -#else -#define Curl_splayprint(x,y,z) Curl_nop_stmt -#endif - #endif /* HEADER_CURL_SPLAY_H */ diff --git a/Utilities/cmcurl/lib/strcase.c b/Utilities/cmcurl/lib/strcase.c index 24bcca93270..a309e352910 100644 --- a/Utilities/cmcurl/lib/strcase.c +++ b/Utilities/cmcurl/lib/strcase.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,8 @@ #include "strcase.h" +static char raw_tolower(char in); + /* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because its behavior is altered by the current locale. */ char Curl_raw_toupper(char in) @@ -93,6 +95,75 @@ char Curl_raw_toupper(char in) return in; } + +/* Portable, consistent tolower (remember EBCDIC). Do not use tolower() because + its behavior is altered by the current locale. */ +static char raw_tolower(char in) +{ +#if !defined(CURL_DOES_CONVERSIONS) + if(in >= 'A' && in <= 'Z') + return (char)('a' + in - 'A'); +#else + switch(in) { + case 'A': + return 'a'; + case 'B': + return 'b'; + case 'C': + return 'c'; + case 'D': + return 'd'; + case 'E': + return 'e'; + case 'F': + return 'f'; + case 'G': + return 'g'; + case 'H': + return 'h'; + case 'I': + return 'i'; + case 'J': + return 'j'; + case 'K': + return 'k'; + case 'L': + return 'l'; + case 'M': + return 'm'; + case 'N': + return 'n'; + case 'O': + return 'o'; + case 'P': + return 'p'; + case 'Q': + return 'q'; + case 'R': + return 'r'; + case 'S': + return 's'; + case 'T': + return 't'; + case 'U': + return 'u'; + case 'V': + return 'v'; + case 'W': + return 'w'; + case 'X': + return 'x'; + case 'Y': + return 'y'; + case 'Z': + return 'z'; + } +#endif + + return in; +} + + /* * Curl_strcasecompare() is for doing "raw" case insensitive strings. This is * meant to be locale independent and only compare strings we know are safe @@ -165,6 +236,21 @@ void Curl_strntoupper(char *dest, const char *src, size_t n) } while(*src++ && --n); } +/* Copy a lower case version of the string from src to dest. The + * strings may overlap. No more than n characters of the string are copied + * (including any NUL) and the destination string will NOT be + * NUL-terminated if that limit is reached. + */ +void Curl_strntolower(char *dest, const char *src, size_t n) +{ + if(n < 1) + return; + + do { + *dest++ = raw_tolower(*src); + } while(*src++ && --n); +} + /* --- public functions --- */ int curl_strequal(const char *first, const char *second) diff --git a/Utilities/cmcurl/lib/strcase.h b/Utilities/cmcurl/lib/strcase.h index 6fee3840e69..cd4c4191a9a 100644 --- a/Utilities/cmcurl/lib/strcase.h +++ b/Utilities/cmcurl/lib/strcase.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -46,5 +46,6 @@ char Curl_raw_toupper(char in); #define checkprefix(a,b) curl_strnequal(a,b,strlen(a)) void Curl_strntoupper(char *dest, const char *src, size_t n); +void Curl_strntolower(char *dest, const char *src, size_t n); #endif /* HEADER_CURL_STRCASE_H */ diff --git a/Utilities/cmcurl/lib/strdup.c b/Utilities/cmcurl/lib/strdup.c index 51e7978b7f1..1ab10fd644e 100644 --- a/Utilities/cmcurl/lib/strdup.c +++ b/Utilities/cmcurl/lib/strdup.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index e273f3765c4..015e588cf7f 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2004 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2004 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -311,6 +311,15 @@ curl_easy_strerror(CURLcode error) case CURLE_RECURSIVE_API_CALL: return "API function called from within callback"; + case CURLE_AUTH_ERROR: + return "An authentication function returned an error"; + + case CURLE_HTTP3: + return "HTTP/3 error"; + + case CURLE_QUIC_CONNECT_ERROR: + return "QUIC connection error"; + /* error codes not used by current libcurl */ case CURLE_OBSOLETE20: case CURLE_OBSOLETE24: @@ -383,6 +392,12 @@ curl_multi_strerror(CURLMcode error) case CURLM_RECURSIVE_API_CALL: return "API function called from within callback"; + case CURLM_WAKEUP_FAILURE: + return "Wakeup is unavailable or failed"; + + case CURLM_BAD_FUNCTION_ARGUMENT: + return "A libcurl function was given a bad argument"; + case CURLM_LAST: break; } @@ -433,19 +448,26 @@ curl_share_strerror(CURLSHcode error) } #ifdef USE_WINSOCK - -/* This function handles most / all (?) Winsock errors curl is able to produce. +/* This is a helper function for Curl_strerror that converts Winsock error + * codes (WSAGetLastError) to error messages. + * Returns NULL if no error message was found for error code. */ static const char * get_winsock_error (int err, char *buf, size_t len) { -#ifdef PRESERVE_WINDOWS_ERROR_CODE - DWORD old_win_err = GetLastError(); -#endif - int old_errno = errno; +#ifndef CURL_DISABLE_VERBOSE_STRINGS const char *p; +#endif -#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(!len) + return NULL; + + *buf = '\0'; + +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)err; + return NULL; +#else switch(err) { case WSAEINTR: p = "Call interrupted"; @@ -614,26 +636,63 @@ get_winsock_error (int err, char *buf, size_t len) default: return NULL; } -#else - if(!err) - return NULL; - else - p = "error"; -#endif strncpy(buf, p, len); buf [len-1] = '\0'; + return buf; +#endif +} +#endif /* USE_WINSOCK */ - if(errno != old_errno) - errno = old_errno; +#if defined(WIN32) || defined(_WIN32_WCE) +/* This is a helper function for Curl_strerror that converts Windows API error + * codes (GetLastError) to error messages. + * Returns NULL if no error message was found for error code. + */ +static const char * +get_winapi_error(int err, char *buf, size_t buflen) +{ + char *p; -#ifdef PRESERVE_WINDOWS_ERROR_CODE - if(old_win_err != GetLastError()) - SetLastError(old_win_err); + if(!buflen) + return NULL; + + *buf = '\0'; + +#ifdef _WIN32_WCE + { + wchar_t wbuf[256]; + wbuf[0] = L'\0'; + + if(FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err, + LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) { + size_t written = wcstombs(buf, wbuf, buflen - 1); + if(written != (size_t)-1) + buf[written] = '\0'; + else + *buf = '\0'; + } + } +#else + if(!FormatMessageA((FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err, + LANG_NEUTRAL, buf, (DWORD)buflen, NULL)) { + *buf = '\0'; + } #endif - return buf; + /* Truncate multiple lines */ + p = strchr(buf, '\n'); + if(p) { + if(p > buf && *(p-1) == '\r') + *(p-1) = '\0'; + else + *p = '\0'; + } + + return (*buf ? buf : NULL); } -#endif /* USE_WINSOCK */ +#endif /* WIN32 || _WIN32_WCE */ /* * Our thread-safe and smart strerror() replacement. @@ -645,6 +704,14 @@ get_winsock_error (int err, char *buf, size_t len) * * We don't do range checking (on systems other than Windows) since there is * no good reliable and portable way to do it. + * + * On Windows different types of error codes overlap. This function has an + * order of preference when trying to match error codes: + * CRT (errno), Winsock (WSAGetLastError), Windows API (GetLastError). + * + * It may be more correct to call one of the variant functions instead: + * Call Curl_sspi_strerror if the error code is definitely Windows SSPI. + * Call Curl_winapi_strerror if the error code is definitely Windows API. */ const char *Curl_strerror(int err, char *buf, size_t buflen) { @@ -655,35 +722,30 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) char *p; size_t max; + if(!buflen) + return NULL; + DEBUGASSERT(err >= 0); max = buflen - 1; *buf = '\0'; -#ifdef USE_WINSOCK - -#ifdef _WIN32_WCE - { - wchar_t wbuf[256]; - wbuf[0] = L'\0'; - - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, - LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL); - wcstombs(buf, wbuf, max); - } -#else +#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(WIN32) /* 'sys_nerr' is the maximum errno number, it is not widely portable */ if(err >= 0 && err < sys_nerr) strncpy(buf, strerror(err), max); - else { - if(!get_winsock_error(err, buf, max) && - !FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, - LANG_NEUTRAL, buf, (DWORD)max, NULL)) + else +#endif + { + if( +#ifdef USE_WINSOCK + !get_winsock_error(err, buf, max) && +#endif + !get_winapi_error((DWORD)err, buf, max)) msnprintf(buf, max, "Unknown error %d (%#x)", err, err); } -#endif - -#else /* not USE_WINSOCK coming up */ +#else /* not Windows coming up */ #if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R) /* @@ -731,9 +793,9 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) } #endif -#endif /* end of ! USE_WINSOCK */ +#endif /* end of not Windows */ - buf[max] = '\0'; /* make sure the string is zero terminated */ + buf[max] = '\0'; /* make sure the string is null-terminated */ /* strip trailing '\r\n' or '\n'. */ p = strrchr(buf, '\n'); @@ -754,7 +816,52 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) return buf; } +/* + * Curl_winapi_strerror: + * Variant of Curl_strerror if the error code is definitely Windows API. + */ +#if defined(WIN32) || defined(_WIN32_WCE) +const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) +{ +#ifdef PRESERVE_WINDOWS_ERROR_CODE + DWORD old_win_err = GetLastError(); +#endif + int old_errno = errno; + + if(!buflen) + return NULL; + + *buf = '\0'; + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(!get_winapi_error(err, buf, buflen)) { + msnprintf(buf, buflen, "Unknown error %u (0x%08X)", err, err); + } +#else + { + const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error"; + strncpy(buf, txt, buflen); + buf[buflen - 1] = '\0'; + } +#endif + + if(errno != old_errno) + errno = old_errno; + +#ifdef PRESERVE_WINDOWS_ERROR_CODE + if(old_win_err != GetLastError()) + SetLastError(old_win_err); +#endif + + return buf; +} +#endif /* WIN32 || _WIN32_WCE */ + #ifdef USE_WINDOWS_SSPI +/* + * Curl_sspi_strerror: + * Variant of Curl_strerror if the error code is definitely Windows SSPI. + */ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen) { #ifdef PRESERVE_WINDOWS_ERROR_CODE @@ -762,18 +869,11 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen) #endif int old_errno = errno; const char *txt; - char *outbuf; - size_t outmax; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char txtbuf[80]; - char msgbuf[256]; - char *p, *str, *msg = NULL; - bool msg_formatted = FALSE; -#endif - outbuf = buf; - outmax = buflen - 1; - *outbuf = '\0'; + if(!buflen) + return NULL; + + *buf = '\0'; #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -781,314 +881,121 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen) case SEC_E_OK: txt = "No error"; break; - case CRYPT_E_REVOKED: - txt = "CRYPT_E_REVOKED"; - break; - case SEC_E_ALGORITHM_MISMATCH: - txt = "SEC_E_ALGORITHM_MISMATCH"; - break; - case SEC_E_BAD_BINDINGS: - txt = "SEC_E_BAD_BINDINGS"; - break; - case SEC_E_BAD_PKGID: - txt = "SEC_E_BAD_PKGID"; - break; - case SEC_E_BUFFER_TOO_SMALL: - txt = "SEC_E_BUFFER_TOO_SMALL"; - break; - case SEC_E_CANNOT_INSTALL: - txt = "SEC_E_CANNOT_INSTALL"; - break; - case SEC_E_CANNOT_PACK: - txt = "SEC_E_CANNOT_PACK"; - break; - case SEC_E_CERT_EXPIRED: - txt = "SEC_E_CERT_EXPIRED"; - break; - case SEC_E_CERT_UNKNOWN: - txt = "SEC_E_CERT_UNKNOWN"; - break; - case SEC_E_CERT_WRONG_USAGE: - txt = "SEC_E_CERT_WRONG_USAGE"; - break; - case SEC_E_CONTEXT_EXPIRED: - txt = "SEC_E_CONTEXT_EXPIRED"; - break; - case SEC_E_CROSSREALM_DELEGATION_FAILURE: - txt = "SEC_E_CROSSREALM_DELEGATION_FAILURE"; - break; - case SEC_E_CRYPTO_SYSTEM_INVALID: - txt = "SEC_E_CRYPTO_SYSTEM_INVALID"; - break; - case SEC_E_DECRYPT_FAILURE: - txt = "SEC_E_DECRYPT_FAILURE"; - break; - case SEC_E_DELEGATION_POLICY: - txt = "SEC_E_DELEGATION_POLICY"; - break; - case SEC_E_DELEGATION_REQUIRED: - txt = "SEC_E_DELEGATION_REQUIRED"; - break; - case SEC_E_DOWNGRADE_DETECTED: - txt = "SEC_E_DOWNGRADE_DETECTED"; - break; - case SEC_E_ENCRYPT_FAILURE: - txt = "SEC_E_ENCRYPT_FAILURE"; - break; - case SEC_E_ILLEGAL_MESSAGE: - txt = "SEC_E_ILLEGAL_MESSAGE"; - break; - case SEC_E_INCOMPLETE_CREDENTIALS: - txt = "SEC_E_INCOMPLETE_CREDENTIALS"; - break; - case SEC_E_INCOMPLETE_MESSAGE: - txt = "SEC_E_INCOMPLETE_MESSAGE"; - break; - case SEC_E_INSUFFICIENT_MEMORY: - txt = "SEC_E_INSUFFICIENT_MEMORY"; - break; - case SEC_E_INTERNAL_ERROR: - txt = "SEC_E_INTERNAL_ERROR"; - break; - case SEC_E_INVALID_HANDLE: - txt = "SEC_E_INVALID_HANDLE"; - break; - case SEC_E_INVALID_PARAMETER: - txt = "SEC_E_INVALID_PARAMETER"; - break; - case SEC_E_INVALID_TOKEN: - txt = "SEC_E_INVALID_TOKEN"; - break; - case SEC_E_ISSUING_CA_UNTRUSTED: - txt = "SEC_E_ISSUING_CA_UNTRUSTED"; - break; - case SEC_E_ISSUING_CA_UNTRUSTED_KDC: - txt = "SEC_E_ISSUING_CA_UNTRUSTED_KDC"; - break; - case SEC_E_KDC_CERT_EXPIRED: - txt = "SEC_E_KDC_CERT_EXPIRED"; - break; - case SEC_E_KDC_CERT_REVOKED: - txt = "SEC_E_KDC_CERT_REVOKED"; - break; - case SEC_E_KDC_INVALID_REQUEST: - txt = "SEC_E_KDC_INVALID_REQUEST"; - break; - case SEC_E_KDC_UNABLE_TO_REFER: - txt = "SEC_E_KDC_UNABLE_TO_REFER"; - break; - case SEC_E_KDC_UNKNOWN_ETYPE: - txt = "SEC_E_KDC_UNKNOWN_ETYPE"; - break; - case SEC_E_LOGON_DENIED: - txt = "SEC_E_LOGON_DENIED"; - break; - case SEC_E_MAX_REFERRALS_EXCEEDED: - txt = "SEC_E_MAX_REFERRALS_EXCEEDED"; - break; - case SEC_E_MESSAGE_ALTERED: - txt = "SEC_E_MESSAGE_ALTERED"; - break; - case SEC_E_MULTIPLE_ACCOUNTS: - txt = "SEC_E_MULTIPLE_ACCOUNTS"; - break; - case SEC_E_MUST_BE_KDC: - txt = "SEC_E_MUST_BE_KDC"; - break; - case SEC_E_NOT_OWNER: - txt = "SEC_E_NOT_OWNER"; - break; - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - txt = "SEC_E_NO_AUTHENTICATING_AUTHORITY"; - break; - case SEC_E_NO_CREDENTIALS: - txt = "SEC_E_NO_CREDENTIALS"; - break; - case SEC_E_NO_IMPERSONATION: - txt = "SEC_E_NO_IMPERSONATION"; - break; - case SEC_E_NO_IP_ADDRESSES: - txt = "SEC_E_NO_IP_ADDRESSES"; - break; - case SEC_E_NO_KERB_KEY: - txt = "SEC_E_NO_KERB_KEY"; - break; - case SEC_E_NO_PA_DATA: - txt = "SEC_E_NO_PA_DATA"; - break; - case SEC_E_NO_S4U_PROT_SUPPORT: - txt = "SEC_E_NO_S4U_PROT_SUPPORT"; - break; - case SEC_E_NO_TGT_REPLY: - txt = "SEC_E_NO_TGT_REPLY"; - break; - case SEC_E_OUT_OF_SEQUENCE: - txt = "SEC_E_OUT_OF_SEQUENCE"; - break; - case SEC_E_PKINIT_CLIENT_FAILURE: - txt = "SEC_E_PKINIT_CLIENT_FAILURE"; - break; - case SEC_E_PKINIT_NAME_MISMATCH: - txt = "SEC_E_PKINIT_NAME_MISMATCH"; - break; - case SEC_E_POLICY_NLTM_ONLY: - txt = "SEC_E_POLICY_NLTM_ONLY"; - break; - case SEC_E_QOP_NOT_SUPPORTED: - txt = "SEC_E_QOP_NOT_SUPPORTED"; - break; - case SEC_E_REVOCATION_OFFLINE_C: - txt = "SEC_E_REVOCATION_OFFLINE_C"; - break; - case SEC_E_REVOCATION_OFFLINE_KDC: - txt = "SEC_E_REVOCATION_OFFLINE_KDC"; - break; - case SEC_E_SECPKG_NOT_FOUND: - txt = "SEC_E_SECPKG_NOT_FOUND"; - break; - case SEC_E_SECURITY_QOS_FAILED: - txt = "SEC_E_SECURITY_QOS_FAILED"; - break; - case SEC_E_SHUTDOWN_IN_PROGRESS: - txt = "SEC_E_SHUTDOWN_IN_PROGRESS"; - break; - case SEC_E_SMARTCARD_CERT_EXPIRED: - txt = "SEC_E_SMARTCARD_CERT_EXPIRED"; - break; - case SEC_E_SMARTCARD_CERT_REVOKED: - txt = "SEC_E_SMARTCARD_CERT_REVOKED"; - break; - case SEC_E_SMARTCARD_LOGON_REQUIRED: - txt = "SEC_E_SMARTCARD_LOGON_REQUIRED"; - break; - case SEC_E_STRONG_CRYPTO_NOT_SUPPORTED: - txt = "SEC_E_STRONG_CRYPTO_NOT_SUPPORTED"; - break; - case SEC_E_TARGET_UNKNOWN: - txt = "SEC_E_TARGET_UNKNOWN"; - break; - case SEC_E_TIME_SKEW: - txt = "SEC_E_TIME_SKEW"; - break; - case SEC_E_TOO_MANY_PRINCIPALS: - txt = "SEC_E_TOO_MANY_PRINCIPALS"; - break; - case SEC_E_UNFINISHED_CONTEXT_DELETED: - txt = "SEC_E_UNFINISHED_CONTEXT_DELETED"; - break; - case SEC_E_UNKNOWN_CREDENTIALS: - txt = "SEC_E_UNKNOWN_CREDENTIALS"; - break; - case SEC_E_UNSUPPORTED_FUNCTION: - txt = "SEC_E_UNSUPPORTED_FUNCTION"; - break; - case SEC_E_UNSUPPORTED_PREAUTH: - txt = "SEC_E_UNSUPPORTED_PREAUTH"; - break; - case SEC_E_UNTRUSTED_ROOT: - txt = "SEC_E_UNTRUSTED_ROOT"; - break; - case SEC_E_WRONG_CREDENTIAL_HANDLE: - txt = "SEC_E_WRONG_CREDENTIAL_HANDLE"; - break; - case SEC_E_WRONG_PRINCIPAL: - txt = "SEC_E_WRONG_PRINCIPAL"; - break; - case SEC_I_COMPLETE_AND_CONTINUE: - txt = "SEC_I_COMPLETE_AND_CONTINUE"; - break; - case SEC_I_COMPLETE_NEEDED: - txt = "SEC_I_COMPLETE_NEEDED"; - break; - case SEC_I_CONTEXT_EXPIRED: - txt = "SEC_I_CONTEXT_EXPIRED"; - break; - case SEC_I_CONTINUE_NEEDED: - txt = "SEC_I_CONTINUE_NEEDED"; - break; - case SEC_I_INCOMPLETE_CREDENTIALS: - txt = "SEC_I_INCOMPLETE_CREDENTIALS"; - break; - case SEC_I_LOCAL_LOGON: - txt = "SEC_I_LOCAL_LOGON"; - break; - case SEC_I_NO_LSA_CONTEXT: - txt = "SEC_I_NO_LSA_CONTEXT"; - break; - case SEC_I_RENEGOTIATE: - txt = "SEC_I_RENEGOTIATE"; - break; - case SEC_I_SIGNATURE_NEEDED: - txt = "SEC_I_SIGNATURE_NEEDED"; - break; +#define SEC2TXT(sec) case sec: txt = #sec; break + SEC2TXT(CRYPT_E_REVOKED); + SEC2TXT(SEC_E_ALGORITHM_MISMATCH); + SEC2TXT(SEC_E_BAD_BINDINGS); + SEC2TXT(SEC_E_BAD_PKGID); + SEC2TXT(SEC_E_BUFFER_TOO_SMALL); + SEC2TXT(SEC_E_CANNOT_INSTALL); + SEC2TXT(SEC_E_CANNOT_PACK); + SEC2TXT(SEC_E_CERT_EXPIRED); + SEC2TXT(SEC_E_CERT_UNKNOWN); + SEC2TXT(SEC_E_CERT_WRONG_USAGE); + SEC2TXT(SEC_E_CONTEXT_EXPIRED); + SEC2TXT(SEC_E_CROSSREALM_DELEGATION_FAILURE); + SEC2TXT(SEC_E_CRYPTO_SYSTEM_INVALID); + SEC2TXT(SEC_E_DECRYPT_FAILURE); + SEC2TXT(SEC_E_DELEGATION_POLICY); + SEC2TXT(SEC_E_DELEGATION_REQUIRED); + SEC2TXT(SEC_E_DOWNGRADE_DETECTED); + SEC2TXT(SEC_E_ENCRYPT_FAILURE); + SEC2TXT(SEC_E_ILLEGAL_MESSAGE); + SEC2TXT(SEC_E_INCOMPLETE_CREDENTIALS); + SEC2TXT(SEC_E_INCOMPLETE_MESSAGE); + SEC2TXT(SEC_E_INSUFFICIENT_MEMORY); + SEC2TXT(SEC_E_INTERNAL_ERROR); + SEC2TXT(SEC_E_INVALID_HANDLE); + SEC2TXT(SEC_E_INVALID_PARAMETER); + SEC2TXT(SEC_E_INVALID_TOKEN); + SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED); + SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED_KDC); + SEC2TXT(SEC_E_KDC_CERT_EXPIRED); + SEC2TXT(SEC_E_KDC_CERT_REVOKED); + SEC2TXT(SEC_E_KDC_INVALID_REQUEST); + SEC2TXT(SEC_E_KDC_UNABLE_TO_REFER); + SEC2TXT(SEC_E_KDC_UNKNOWN_ETYPE); + SEC2TXT(SEC_E_LOGON_DENIED); + SEC2TXT(SEC_E_MAX_REFERRALS_EXCEEDED); + SEC2TXT(SEC_E_MESSAGE_ALTERED); + SEC2TXT(SEC_E_MULTIPLE_ACCOUNTS); + SEC2TXT(SEC_E_MUST_BE_KDC); + SEC2TXT(SEC_E_NOT_OWNER); + SEC2TXT(SEC_E_NO_AUTHENTICATING_AUTHORITY); + SEC2TXT(SEC_E_NO_CREDENTIALS); + SEC2TXT(SEC_E_NO_IMPERSONATION); + SEC2TXT(SEC_E_NO_IP_ADDRESSES); + SEC2TXT(SEC_E_NO_KERB_KEY); + SEC2TXT(SEC_E_NO_PA_DATA); + SEC2TXT(SEC_E_NO_S4U_PROT_SUPPORT); + SEC2TXT(SEC_E_NO_TGT_REPLY); + SEC2TXT(SEC_E_OUT_OF_SEQUENCE); + SEC2TXT(SEC_E_PKINIT_CLIENT_FAILURE); + SEC2TXT(SEC_E_PKINIT_NAME_MISMATCH); + SEC2TXT(SEC_E_POLICY_NLTM_ONLY); + SEC2TXT(SEC_E_QOP_NOT_SUPPORTED); + SEC2TXT(SEC_E_REVOCATION_OFFLINE_C); + SEC2TXT(SEC_E_REVOCATION_OFFLINE_KDC); + SEC2TXT(SEC_E_SECPKG_NOT_FOUND); + SEC2TXT(SEC_E_SECURITY_QOS_FAILED); + SEC2TXT(SEC_E_SHUTDOWN_IN_PROGRESS); + SEC2TXT(SEC_E_SMARTCARD_CERT_EXPIRED); + SEC2TXT(SEC_E_SMARTCARD_CERT_REVOKED); + SEC2TXT(SEC_E_SMARTCARD_LOGON_REQUIRED); + SEC2TXT(SEC_E_STRONG_CRYPTO_NOT_SUPPORTED); + SEC2TXT(SEC_E_TARGET_UNKNOWN); + SEC2TXT(SEC_E_TIME_SKEW); + SEC2TXT(SEC_E_TOO_MANY_PRINCIPALS); + SEC2TXT(SEC_E_UNFINISHED_CONTEXT_DELETED); + SEC2TXT(SEC_E_UNKNOWN_CREDENTIALS); + SEC2TXT(SEC_E_UNSUPPORTED_FUNCTION); + SEC2TXT(SEC_E_UNSUPPORTED_PREAUTH); + SEC2TXT(SEC_E_UNTRUSTED_ROOT); + SEC2TXT(SEC_E_WRONG_CREDENTIAL_HANDLE); + SEC2TXT(SEC_E_WRONG_PRINCIPAL); + SEC2TXT(SEC_I_COMPLETE_AND_CONTINUE); + SEC2TXT(SEC_I_COMPLETE_NEEDED); + SEC2TXT(SEC_I_CONTEXT_EXPIRED); + SEC2TXT(SEC_I_CONTINUE_NEEDED); + SEC2TXT(SEC_I_INCOMPLETE_CREDENTIALS); + SEC2TXT(SEC_I_LOCAL_LOGON); + SEC2TXT(SEC_I_NO_LSA_CONTEXT); + SEC2TXT(SEC_I_RENEGOTIATE); + SEC2TXT(SEC_I_SIGNATURE_NEEDED); default: txt = "Unknown error"; } - if(err == SEC_E_OK) - strncpy(outbuf, txt, outmax); - else if(err == SEC_E_ILLEGAL_MESSAGE) - msnprintf(outbuf, outmax, + if(err == SEC_E_ILLEGAL_MESSAGE) { + msnprintf(buf, buflen, "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually occurs " "when a fatal SSL/TLS alert is received (e.g. handshake failed)." " More detail may be available in the Windows System event log.", err); + } else { - str = txtbuf; + char txtbuf[80]; + char msgbuf[256]; + msnprintf(txtbuf, sizeof(txtbuf), "%s (0x%08X)", txt, err); - txtbuf[sizeof(txtbuf)-1] = '\0'; -#ifdef _WIN32_WCE - { - wchar_t wbuf[256]; - wbuf[0] = L'\0'; - - if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, err, LANG_NEUTRAL, - wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) { - wcstombs(msgbuf, wbuf, sizeof(msgbuf)-1); - msg_formatted = TRUE; - } - } -#else - if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, err, LANG_NEUTRAL, - msgbuf, sizeof(msgbuf)-1, NULL)) { - msg_formatted = TRUE; + if(get_winapi_error(err, msgbuf, sizeof(msgbuf))) + msnprintf(buf, buflen, "%s - %s", txtbuf, msgbuf); + else { + strncpy(buf, txtbuf, buflen); + buf[buflen - 1] = '\0'; } -#endif - if(msg_formatted) { - msgbuf[sizeof(msgbuf)-1] = '\0'; - /* strip trailing '\r\n' or '\n' */ - p = strrchr(msgbuf, '\n'); - if(p && (p - msgbuf) >= 2) - *p = '\0'; - p = strrchr(msgbuf, '\r'); - if(p && (p - msgbuf) >= 1) - *p = '\0'; - msg = msgbuf; - } - if(msg) - msnprintf(outbuf, outmax, "%s - %s", str, msg); - else - strncpy(outbuf, str, outmax); } #else - if(err == SEC_E_OK) txt = "No error"; else txt = "Error"; - - strncpy(outbuf, txt, outmax); - + strncpy(buf, txt, buflen); + buf[buflen - 1] = '\0'; #endif - outbuf[outmax] = '\0'; - if(errno != old_errno) errno = old_errno; @@ -1097,6 +1004,6 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen) SetLastError(old_win_err); #endif - return outbuf; + return buf; } #endif /* USE_WINDOWS_SSPI */ diff --git a/Utilities/cmcurl/lib/strerror.h b/Utilities/cmcurl/lib/strerror.h index 683b5b4a3a8..bae8f897454 100644 --- a/Utilities/cmcurl/lib/strerror.h +++ b/Utilities/cmcurl/lib/strerror.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,9 +24,12 @@ #include "urldata.h" -#define STRERROR_LEN 128 /* a suitable length */ +#define STRERROR_LEN 256 /* a suitable length */ const char *Curl_strerror(int err, char *buf, size_t buflen); +#if defined(WIN32) || defined(_WIN32_WCE) +const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen); +#endif #ifdef USE_WINDOWS_SSPI const char *Curl_sspi_strerror(int err, char *buf, size_t buflen); #endif diff --git a/Utilities/cmcurl/lib/strtok.c b/Utilities/cmcurl/lib/strtok.c index 460eb87e51c..ba6e0258a69 100644 --- a/Utilities/cmcurl/lib/strtok.c +++ b/Utilities/cmcurl/lib/strtok.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -52,7 +52,7 @@ Curl_strtok_r(char *ptr, const char *sep, char **end) if(**end) { /* the end is not a null byte */ - **end = '\0'; /* zero terminate it! */ + **end = '\0'; /* null-terminate it! */ ++*end; /* advance the last pointer to beyond the null byte */ } diff --git a/Utilities/cmcurl/lib/strtok.h b/Utilities/cmcurl/lib/strtok.h index 90b831eb67d..e221fa680f9 100644 --- a/Utilities/cmcurl/lib/strtok.h +++ b/Utilities/cmcurl/lib/strtok.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/strtoofft.c b/Utilities/cmcurl/lib/strtoofft.c index 546a3ff75d6..96e3820600e 100644 --- a/Utilities/cmcurl/lib/strtoofft.c +++ b/Utilities/cmcurl/lib/strtoofft.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c index f7f817dd45d..b9587b5f3af 100644 --- a/Utilities/cmcurl/lib/system_win32.c +++ b/Utilities/cmcurl/lib/system_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2017, Steve Holme, . + * Copyright (C) 2016 - 2020, Steve Holme, . * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -36,6 +36,12 @@ LARGE_INTEGER Curl_freq; bool Curl_isVistaOrGreater; +/* Handle of iphlpapp.dll */ +static HMODULE s_hIpHlpApiDll = NULL; + +/* Pointer to the if_nametoindex function */ +IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL; + /* Curl_win32_init() performs win32 global initialization */ CURLcode Curl_win32_init(long flags) { @@ -89,20 +95,37 @@ CURLcode Curl_win32_init(long flags) } #endif + s_hIpHlpApiDll = Curl_load_library(TEXT("iphlpapi.dll")); + if(s_hIpHlpApiDll) { + /* Get the address of the if_nametoindex function */ + IF_NAMETOINDEX_FN pIfNameToIndex = + CURLX_FUNCTION_CAST(IF_NAMETOINDEX_FN, + (GetProcAddress(s_hIpHlpApiDll, "if_nametoindex"))); + + if(pIfNameToIndex) + Curl_if_nametoindex = pIfNameToIndex; + } + if(Curl_verify_windows_version(6, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { Curl_isVistaOrGreater = TRUE; - QueryPerformanceFrequency(&Curl_freq); } else Curl_isVistaOrGreater = FALSE; + QueryPerformanceFrequency(&Curl_freq); return CURLE_OK; } /* Curl_win32_cleanup() is the opposite of Curl_win32_init() */ void Curl_win32_cleanup(long init_flags) { + if(s_hIpHlpApiDll) { + FreeLibrary(s_hIpHlpApiDll); + s_hIpHlpApiDll = NULL; + Curl_if_nametoindex = NULL; + } + #ifdef USE_WINDOWS_SSPI Curl_sspi_global_cleanup(); #endif @@ -114,10 +137,6 @@ void Curl_win32_cleanup(long init_flags) } } -#if defined(USE_WINDOWS_SSPI) || (!defined(CURL_DISABLE_TELNET) && \ - defined(USE_WINSOCK)) - - #if !defined(LOAD_WITH_ALTERED_SEARCH_PATH) #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 #endif @@ -140,8 +159,6 @@ typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD); # define LOADLIBARYEX "LoadLibraryExA" #endif -#endif /* USE_WINDOWS_SSPI || (!CURL_DISABLE_TELNET && USE_WINSOCK) */ - /* * Curl_verify_windows_version() * @@ -334,9 +351,6 @@ bool Curl_verify_windows_version(const unsigned int majorVersion, return matched; } -#if defined(USE_WINDOWS_SSPI) || (!defined(CURL_DISABLE_TELNET) && \ - defined(USE_WINSOCK)) - /* * Curl_load_library() * @@ -353,6 +367,7 @@ bool Curl_verify_windows_version(const unsigned int majorVersion, */ HMODULE Curl_load_library(LPCTSTR filename) { +#ifndef CURL_WINDOWS_APP HMODULE hModule = NULL; LOADLIBRARYEX_FN pLoadLibraryEx = NULL; @@ -407,10 +422,12 @@ HMODULE Curl_load_library(LPCTSTR filename) free(path); } } - return hModule; +#else + /* the Universal Windows Platform (UWP) can't do this */ + (void)filename; + return NULL; +#endif } -#endif /* USE_WINDOWS_SSPI || (!CURL_DISABLE_TELNET && USE_WINSOCK) */ - #endif /* WIN32 */ diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h index 926328a9aae..d2882fce1ae 100644 --- a/Utilities/cmcurl/lib/system_win32.h +++ b/Utilities/cmcurl/lib/system_win32.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016, Steve Holme, . + * Copyright (C) 2016 - 2019, Steve Holme, . * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -48,20 +48,21 @@ typedef enum { PLATFORM_WINNT } PlatformIdentifier; +/* We use our own typedef here since some headers might lack this */ +typedef unsigned int(WINAPI *IF_NAMETOINDEX_FN)(const char *); + +/* This is used instead of if_nametoindex if available on Windows */ +extern IF_NAMETOINDEX_FN Curl_if_nametoindex; + /* This is used to verify if we are running on a specific windows version */ bool Curl_verify_windows_version(const unsigned int majorVersion, const unsigned int minorVersion, const PlatformIdentifier platform, const VersionCondition condition); -#if defined(USE_WINDOWS_SSPI) || (!defined(CURL_DISABLE_TELNET) && \ - defined(USE_WINSOCK)) - /* This is used to dynamically load DLLs */ HMODULE Curl_load_library(LPCTSTR filename); -#endif /* USE_WINDOWS_SSPI || (!CURL_DISABLE_TELNET && USE_WINSOCK) */ - #endif /* WIN32 */ #endif /* HEADER_CURL_SYSTEM_WIN32_H */ diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c index 955255c36c2..c3b58e54c5b 100644 --- a/Utilities/cmcurl/lib/telnet.c +++ b/Utilities/cmcurl/lib/telnet.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -69,12 +69,12 @@ do { \ x->subend = x->subpointer; \ CURL_SB_CLEAR(x); \ - } WHILE_FALSE + } while(0) #define CURL_SB_ACCUM(x,c) \ do { \ if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \ *x->subpointer++ = (c); \ - } WHILE_FALSE + } while(0) #define CURL_SB_GET(x) ((*x->subpointer++)&0xff) #define CURL_SB_LEN(x) (x->subend - x->subpointer) @@ -1315,7 +1315,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) DWORD readfile_read; int err; #else - int interval_ms; + timediff_t interval_ms; struct pollfd pfd[2]; int poll_cnt; curl_off_t total_dl = 0; diff --git a/Utilities/cmcurl/lib/telnet.h b/Utilities/cmcurl/lib/telnet.h index 668a78a133a..431427f3957 100644 --- a/Utilities/cmcurl/lib/telnet.h +++ b/Utilities/cmcurl/lib/telnet.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index 289cda2825f..378d9560813 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -115,11 +115,11 @@ typedef enum { TFTP_ERR_NORESPONSE } tftp_error_t; -typedef struct tftp_packet { +struct tftp_packet { unsigned char *data; -} tftp_packet_t; +}; -typedef struct tftp_state_data { +struct tftp_state_data { tftp_state_t state; tftp_mode_t mode; tftp_error_t error; @@ -140,25 +140,24 @@ typedef struct tftp_state_data { int sbytes; int blksize; int requested_blksize; - tftp_packet_t rpacket; - tftp_packet_t spacket; -} tftp_state_data_t; + struct tftp_packet rpacket; + struct tftp_packet spacket; +}; /* Forward declarations */ -static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event); -static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event); +static CURLcode tftp_rx(struct tftp_state_data *state, tftp_event_t event); +static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event); static CURLcode tftp_connect(struct connectdata *conn, bool *done); static CURLcode tftp_disconnect(struct connectdata *conn, bool dead_connection); static CURLcode tftp_do(struct connectdata *conn, bool *done); static CURLcode tftp_done(struct connectdata *conn, CURLcode, bool premature); -static CURLcode tftp_setup_connection(struct connectdata * conn); +static CURLcode tftp_setup_connection(struct connectdata *conn); static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done); static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done); -static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); +static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks); static CURLcode tftp_translate_code(tftp_error_t error); @@ -197,7 +196,7 @@ const struct Curl_handler Curl_handler_tftp = { * * **********************************************************/ -static CURLcode tftp_set_timeouts(tftp_state_data_t *state) +static CURLcode tftp_set_timeouts(struct tftp_state_data *state) { time_t maxtime, timeout; timediff_t timeout_ms; @@ -280,25 +279,25 @@ static CURLcode tftp_set_timeouts(tftp_state_data_t *state) * **********************************************************/ -static void setpacketevent(tftp_packet_t *packet, unsigned short num) +static void setpacketevent(struct tftp_packet *packet, unsigned short num) { packet->data[0] = (unsigned char)(num >> 8); packet->data[1] = (unsigned char)(num & 0xff); } -static void setpacketblock(tftp_packet_t *packet, unsigned short num) +static void setpacketblock(struct tftp_packet *packet, unsigned short num) { packet->data[2] = (unsigned char)(num >> 8); packet->data[3] = (unsigned char)(num & 0xff); } -static unsigned short getrpacketevent(const tftp_packet_t *packet) +static unsigned short getrpacketevent(const struct tftp_packet *packet) { return (unsigned short)((packet->data[0] << 8) | packet->data[1]); } -static unsigned short getrpacketblock(const tftp_packet_t *packet) +static unsigned short getrpacketblock(const struct tftp_packet *packet) { return (unsigned short)((packet->data[2] << 8) | packet->data[3]); } @@ -331,7 +330,7 @@ static const char *tftp_option_get(const char *buf, size_t len, return &buf[loc]; } -static CURLcode tftp_parse_option_ack(tftp_state_data_t *state, +static CURLcode tftp_parse_option_ack(struct tftp_state_data *state, const char *ptr, int len) { const char *tmp = ptr; @@ -404,16 +403,17 @@ static CURLcode tftp_parse_option_ack(tftp_state_data_t *state, return CURLE_OK; } -static size_t tftp_option_add(tftp_state_data_t *state, size_t csize, - char *buf, const char *option) +static CURLcode tftp_option_add(struct tftp_state_data *state, size_t *csize, + char *buf, const char *option) { - if(( strlen(option) + csize + 1) > (size_t)state->blksize) - return 0; + if(( strlen(option) + *csize + 1) > (size_t)state->blksize) + return CURLE_TFTP_ILLEGAL; strcpy(buf, option); - return strlen(option) + 1; + *csize += strlen(option) + 1; + return CURLE_OK; } -static CURLcode tftp_connect_for_tx(tftp_state_data_t *state, +static CURLcode tftp_connect_for_tx(struct tftp_state_data *state, tftp_event_t event) { CURLcode result; @@ -429,7 +429,7 @@ static CURLcode tftp_connect_for_tx(tftp_state_data_t *state, return tftp_tx(state, event); } -static CURLcode tftp_connect_for_rx(tftp_state_data_t *state, +static CURLcode tftp_connect_for_rx(struct tftp_state_data *state, tftp_event_t event) { CURLcode result; @@ -445,7 +445,8 @@ static CURLcode tftp_connect_for_rx(tftp_state_data_t *state, return tftp_rx(state, event); } -static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) +static CURLcode tftp_send_first(struct tftp_state_data *state, + tftp_event_t event) { size_t sbytes; ssize_t senddata; @@ -486,7 +487,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) file name so we skip the always-present first letter of the path string. */ result = Curl_urldecode(data, &state->conn->data->state.up.path[1], 0, - &filename, NULL, FALSE); + &filename, NULL, REJECT_ZERO); if(result) return result; @@ -511,26 +512,38 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) else strcpy(buf, "0"); /* the destination is large enough */ - sbytes += tftp_option_add(state, sbytes, - (char *)state->spacket.data + sbytes, - TFTP_OPTION_TSIZE); - sbytes += tftp_option_add(state, sbytes, - (char *)state->spacket.data + sbytes, buf); + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, + TFTP_OPTION_TSIZE); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, buf); + /* add blksize option */ msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); - sbytes += tftp_option_add(state, sbytes, - (char *)state->spacket.data + sbytes, - TFTP_OPTION_BLKSIZE); - sbytes += tftp_option_add(state, sbytes, - (char *)state->spacket.data + sbytes, buf); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, + TFTP_OPTION_BLKSIZE); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, buf); /* add timeout option */ msnprintf(buf, sizeof(buf), "%d", state->retry_time); - sbytes += tftp_option_add(state, sbytes, - (char *)state->spacket.data + sbytes, - TFTP_OPTION_INTERVAL); - sbytes += tftp_option_add(state, sbytes, - (char *)state->spacket.data + sbytes, buf); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, + TFTP_OPTION_INTERVAL); + if(result == CURLE_OK) + result = tftp_option_add(state, &sbytes, + (char *)state->spacket.data + sbytes, buf); + + if(result != CURLE_OK) { + failf(data, "TFTP buffer too small for options"); + free(filename); + return CURLE_TFTP_ILLEGAL; + } } /* the typecase for the 3rd argument is mostly for systems that do @@ -586,7 +599,8 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) * Event handler for the RX state * **********************************************************/ -static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) +static CURLcode tftp_rx(struct tftp_state_data *state, + tftp_event_t event) { ssize_t sbytes; int rblock; @@ -708,7 +722,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) * Event handler for the TX state * **********************************************************/ -static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) +static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) { struct Curl_easy *data = state->conn->data; ssize_t sbytes; @@ -908,7 +922,7 @@ static CURLcode tftp_translate_code(tftp_error_t error) * The tftp state machine event dispatcher * **********************************************************/ -static CURLcode tftp_state_machine(tftp_state_data_t *state, +static CURLcode tftp_state_machine(struct tftp_state_data *state, tftp_event_t event) { CURLcode result = CURLE_OK; @@ -949,7 +963,7 @@ static CURLcode tftp_state_machine(tftp_state_data_t *state, **********************************************************/ static CURLcode tftp_disconnect(struct connectdata *conn, bool dead_connection) { - tftp_state_data_t *state = conn->proto.tftpc; + struct tftp_state_data *state = conn->proto.tftpc; (void) dead_connection; /* done, free dynamically allocated pkt buffers */ @@ -971,12 +985,13 @@ static CURLcode tftp_disconnect(struct connectdata *conn, bool dead_connection) **********************************************************/ static CURLcode tftp_connect(struct connectdata *conn, bool *done) { - tftp_state_data_t *state; + struct tftp_state_data *state; int blksize; + int need_blksize; blksize = TFTP_BLKSIZE_DEFAULT; - state = conn->proto.tftpc = calloc(1, sizeof(tftp_state_data_t)); + state = conn->proto.tftpc = calloc(1, sizeof(struct tftp_state_data)); if(!state) return CURLE_OUT_OF_MEMORY; @@ -987,15 +1002,20 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done) return CURLE_TFTP_ILLEGAL; } + need_blksize = blksize; + /* default size is the fallback when no OACK is received */ + if(need_blksize < TFTP_BLKSIZE_DEFAULT) + need_blksize = TFTP_BLKSIZE_DEFAULT; + if(!state->rpacket.data) { - state->rpacket.data = calloc(1, blksize + 2 + 2); + state->rpacket.data = calloc(1, need_blksize + 2 + 2); if(!state->rpacket.data) return CURLE_OUT_OF_MEMORY; } if(!state->spacket.data) { - state->spacket.data = calloc(1, blksize + 2 + 2); + state->spacket.data = calloc(1, need_blksize + 2 + 2); if(!state->spacket.data) return CURLE_OUT_OF_MEMORY; @@ -1009,7 +1029,7 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done) state->sockfd = state->conn->sock[FIRSTSOCKET]; state->state = TFTP_STATE_START; state->error = TFTP_ERR_NONE; - state->blksize = blksize; + state->blksize = TFTP_BLKSIZE_DEFAULT; /* Unless updated by OACK response */ state->requested_blksize = blksize; ((struct sockaddr *)&state->local_addr)->sa_family = @@ -1060,7 +1080,7 @@ static CURLcode tftp_done(struct connectdata *conn, CURLcode status, bool premature) { CURLcode result = CURLE_OK; - tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; + struct tftp_state_data *state = conn->proto.tftpc; (void)status; /* unused */ (void)premature; /* not used */ @@ -1082,14 +1102,9 @@ static CURLcode tftp_done(struct connectdata *conn, CURLcode status, * The getsock callback * **********************************************************/ -static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks) { - if(!numsocks) - return GETSOCK_BLANK; - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0); } @@ -1106,7 +1121,7 @@ static CURLcode tftp_receive_packet(struct connectdata *conn) curl_socklen_t fromlen; CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; - tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; + struct tftp_state_data *state = conn->proto.tftpc; struct SingleRequest *k = &data->req; /* Receive the packet */ @@ -1193,8 +1208,8 @@ static CURLcode tftp_receive_packet(struct connectdata *conn) **********************************************************/ static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event) { - time_t current; - tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; + time_t current; + struct tftp_state_data *state = conn->proto.tftpc; if(event) *event = TFTP_EVENT_NONE; @@ -1231,7 +1246,7 @@ static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done) tftp_event_t event; CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; - tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; + struct tftp_state_data *state = conn->proto.tftpc; long timeout_ms = tftp_state_timeout(conn, &event); *done = FALSE; @@ -1315,7 +1330,7 @@ static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done) static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done) { CURLcode result = CURLE_OK; - tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; + struct tftp_state_data *state = conn->proto.tftpc; *dophase_done = FALSE; @@ -1345,7 +1360,7 @@ static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done) static CURLcode tftp_do(struct connectdata *conn, bool *done) { - tftp_state_data_t *state; + struct tftp_state_data *state; CURLcode result; *done = FALSE; @@ -1356,7 +1371,7 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) return result; } - state = (tftp_state_data_t *)conn->proto.tftpc; + state = conn->proto.tftpc; if(!state) return CURLE_TFTP_ILLEGAL; @@ -1371,12 +1386,12 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) return result; } -static CURLcode tftp_setup_connection(struct connectdata * conn) +static CURLcode tftp_setup_connection(struct connectdata *conn) { struct Curl_easy *data = conn->data; char *type; - conn->socktype = SOCK_DGRAM; /* UDP datagram based */ + conn->transport = TRNSPRT_UDP; /* TFTP URLs support an extension like ";mode=" that * we'll try to get now! */ diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h index 1335f64bd1a..33348300fed 100644 --- a/Utilities/cmcurl/lib/tftp.h +++ b/Utilities/cmcurl/lib/tftp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c index e2bd7fd143b..e761966a1b0 100644 --- a/Utilities/cmcurl/lib/timeval.c +++ b/Utilities/cmcurl/lib/timeval.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,6 +28,7 @@ extern LARGE_INTEGER Curl_freq; extern bool Curl_isVistaOrGreater; +/* In case of bug fix this function has a counterpart in tool_util.c */ struct curltime Curl_now(void) { struct curltime now; @@ -174,14 +175,6 @@ struct curltime Curl_now(void) #endif -#if SIZEOF_TIME_T < 8 -#define TIME_MAX INT_MAX -#define TIME_MIN INT_MIN -#else -#define TIME_MAX 9223372036854775807LL -#define TIME_MIN -9223372036854775807LL -#endif - /* * Returns: time difference in number of milliseconds. For too large diffs it * returns max value. @@ -191,10 +184,10 @@ struct curltime Curl_now(void) timediff_t Curl_timediff(struct curltime newer, struct curltime older) { timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; - if(diff >= (TIME_MAX/1000)) - return TIME_MAX; - else if(diff <= (TIME_MIN/1000)) - return TIME_MIN; + if(diff >= (TIMEDIFF_T_MAX/1000)) + return TIMEDIFF_T_MAX; + else if(diff <= (TIMEDIFF_T_MIN/1000)) + return TIMEDIFF_T_MIN; return diff * 1000 + (newer.tv_usec-older.tv_usec)/1000; } @@ -205,9 +198,9 @@ timediff_t Curl_timediff(struct curltime newer, struct curltime older) timediff_t Curl_timediff_us(struct curltime newer, struct curltime older) { timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; - if(diff >= (TIME_MAX/1000000)) - return TIME_MAX; - else if(diff <= (TIME_MIN/1000000)) - return TIME_MIN; + if(diff >= (TIMEDIFF_T_MAX/1000000)) + return TIMEDIFF_T_MAX; + else if(diff <= (TIMEDIFF_T_MIN/1000000)) + return TIMEDIFF_T_MIN; return diff * 1000000 + newer.tv_usec-older.tv_usec; } diff --git a/Utilities/cmcurl/lib/timeval.h b/Utilities/cmcurl/lib/timeval.h index 96867d71390..53e063607cf 100644 --- a/Utilities/cmcurl/lib/timeval.h +++ b/Utilities/cmcurl/lib/timeval.h @@ -24,13 +24,13 @@ #include "curl_setup.h" -#if SIZEOF_TIME_T < 8 -typedef int timediff_t; -#define CURL_FORMAT_TIMEDIFF_T "d" -#else +/* Use a larger type even for 32 bit time_t systems so that we can keep + microsecond accuracy in it */ typedef curl_off_t timediff_t; #define CURL_FORMAT_TIMEDIFF_T CURL_FORMAT_CURL_OFF_T -#endif + +#define TIMEDIFF_T_MAX CURL_OFF_T_MAX +#define TIMEDIFF_T_MIN CURL_OFF_T_MIN struct curltime { time_t tv_sec; /* seconds */ diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 514330e8c1d..133a4783c67 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -48,6 +48,8 @@ #ifdef HAVE_SYS_SELECT_H #include +#elif defined(HAVE_UNISTD_H) +#include #endif #ifndef HAVE_SOCKET @@ -126,12 +128,13 @@ static size_t Curl_trailers_read(char *buffer, size_t size, size_t nitems, void *raw) { struct Curl_easy *data = (struct Curl_easy *)raw; - Curl_send_buffer *trailers_buf = data->state.trailers_buf; - size_t bytes_left = trailers_buf->size_used-data->state.trailers_bytes_sent; + struct dynbuf *trailers_buf = &data->state.trailers_buf; + size_t bytes_left = Curl_dyn_len(trailers_buf) - + data->state.trailers_bytes_sent; size_t to_copy = (size*nitems < bytes_left) ? size*nitems : bytes_left; if(to_copy) { memcpy(buffer, - &trailers_buf->buffer[data->state.trailers_bytes_sent], + Curl_dyn_ptr(trailers_buf) + data->state.trailers_bytes_sent, to_copy); data->state.trailers_bytes_sent += to_copy; } @@ -141,8 +144,8 @@ static size_t Curl_trailers_read(char *buffer, size_t size, size_t nitems, static size_t Curl_trailers_left(void *raw) { struct Curl_easy *data = (struct Curl_easy *)raw; - Curl_send_buffer *trailers_buf = data->state.trailers_buf; - return trailers_buf->size_used - data->state.trailers_bytes_sent; + struct dynbuf *trailers_buf = &data->state.trailers_buf; + return Curl_dyn_len(trailers_buf) - data->state.trailers_bytes_sent; } #endif @@ -176,7 +179,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, #ifndef CURL_DISABLE_HTTP if(data->state.trailers_state == TRAILERS_INITIALIZED) { struct curl_slist *trailers = NULL; - CURLcode c; + CURLcode result; int trailers_ret_code; /* at this point we already verified that the callback exists @@ -184,28 +187,26 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, infof(data, "Moving trailers state machine from initialized to sending.\n"); data->state.trailers_state = TRAILERS_SENDING; - data->state.trailers_buf = Curl_add_buffer_init(); - if(!data->state.trailers_buf) { - failf(data, "Unable to allocate trailing headers buffer !"); - return CURLE_OUT_OF_MEMORY; - } + Curl_dyn_init(&data->state.trailers_buf, DYN_TRAILERS); + data->state.trailers_bytes_sent = 0; Curl_set_in_callback(data, true); trailers_ret_code = data->set.trailer_callback(&trailers, data->set.trailer_data); Curl_set_in_callback(data, false); if(trailers_ret_code == CURL_TRAILERFUNC_OK) { - c = Curl_http_compile_trailers(trailers, data->state.trailers_buf, data); + result = Curl_http_compile_trailers(trailers, &data->state.trailers_buf, + data); } else { failf(data, "operation aborted by trailing headers callback"); *nreadp = 0; - c = CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; } - if(c != CURLE_OK) { - Curl_add_buffer_free(&data->state.trailers_buf); + if(result) { + Curl_dyn_free(&data->state.trailers_buf); curl_slist_free_all(trailers); - return c; + return result; } infof(data, "Successfully compiled trailers.\r\n"); curl_slist_free_all(trailers); @@ -225,7 +226,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, if(data->state.trailers_state == TRAILERS_SENDING) { /* if we're here then that means that we already sent the last empty chunk but we didn't send a final CR LF, so we sent 0 CR LF. We then start - pulling trailing data until we ²have no more at which point we + pulling trailing data until we have no more at which point we simply return to the previous point in the state machine as if nothing happened. */ @@ -366,7 +367,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, #ifndef CURL_DISABLE_HTTP if(data->state.trailers_state == TRAILERS_SENDING && !Curl_trailers_left(data)) { - Curl_add_buffer_free(&data->state.trailers_buf); + Curl_dyn_free(&data->state.trailers_buf); data->state.trailers_state = TRAILERS_DONE; data->set.trailer_data = NULL; data->set.trailer_callback = NULL; @@ -432,8 +433,8 @@ CURLcode Curl_readrewind(struct connectdata *conn) } if(data->set.postfields) ; /* do nothing */ - else if(data->set.httpreq == HTTPREQ_POST_MIME || - data->set.httpreq == HTTPREQ_POST_FORM) { + else if(data->state.httpreq == HTTPREQ_POST_MIME || + data->state.httpreq == HTTPREQ_POST_FORM) { if(Curl_mime_rewind(mimepart)) { failf(data, "Cannot rewind mime/post data"); return CURLE_SEND_FAIL_REWIND; @@ -483,8 +484,9 @@ CURLcode Curl_readrewind(struct connectdata *conn) return CURLE_OK; } -static int data_pending(const struct connectdata *conn) +static int data_pending(const struct Curl_easy *data) { + struct connectdata *conn = data->conn; /* in the case of libssh2, we can never be really sure that we have emptied its internal buffers so we MUST always try until we get EAGAIN back */ return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) || @@ -497,7 +499,9 @@ static int data_pending(const struct connectdata *conn) TRUE. The thing is if we read everything, then http2_recv won't be called and we cannot signal the HTTP/2 stream has closed. As a workaround, we return nonzero here to call http2_recv. */ - ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20); + ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20); +#elif defined(ENABLE_QUIC) + Curl_ssl_data_pending(conn, FIRSTSOCKET) || Curl_quic_data_pending(data); #else Curl_ssl_data_pending(conn, FIRSTSOCKET); #endif @@ -554,6 +558,8 @@ static CURLcode readwrite_data(struct Curl_easy *data, size_t excess = 0; /* excess bytes read */ bool readmore = FALSE; /* used by RTP to signal for more data */ int maxloops = 100; + char *buf = data->state.buffer; + DEBUGASSERT(buf); *done = FALSE; *comeback = FALSE; @@ -564,17 +570,20 @@ static CURLcode readwrite_data(struct Curl_easy *data, bool is_empty_data = FALSE; size_t buffersize = data->set.buffer_size; size_t bytestoread = buffersize; +#ifdef USE_NGHTTP2 + bool is_http2 = ((conn->handler->protocol & PROTO_FAMILY_HTTP) && + (conn->httpversion == 20)); +#endif if( -#if defined(USE_NGHTTP2) +#ifdef USE_NGHTTP2 /* For HTTP/2, read data without caring about the content length. This is safe because body in HTTP/2 is always segmented thanks to its framing layer. Meanwhile, we have to call Curl_read to ensure that http2_handle_stream_close is called when we read all incoming bytes for a particular stream. */ - !((conn->handler->protocol & PROTO_FAMILY_HTTP) && - conn->httpversion == 20) && + !is_http2 && #endif k->size != -1 && !k->header) { /* make sure we don't read too much */ @@ -585,7 +594,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, if(bytestoread) { /* receive data from the network! */ - result = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread); + result = Curl_read(conn, conn->sockfd, buf, bytestoread, &nread); /* read would've blocked */ if(CURLE_AGAIN == result) @@ -601,7 +610,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, nread = 0; } - if((k->bytecount == 0) && (k->writebytecount == 0)) { + if(!k->bytecount) { Curl_pgrsTime(data, TIMER_STARTTRANSFER); if(k->exp100 > EXP100_SEND_DATA) /* set time stamp to compare with when waiting for the 100 */ @@ -612,21 +621,25 @@ static CURLcode readwrite_data(struct Curl_easy *data, /* indicates data of zero size, i.e. empty file */ is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE; - /* NUL terminate, allowing string ops to be used */ if(0 < nread || is_empty_data) { - k->buf[nread] = 0; + buf[nread] = 0; } else { - /* if we receive 0 or less here, the server closed the connection - and we bail out from this! */ - DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n")); + /* if we receive 0 or less here, either the http2 stream is closed or the + server closed the connection and we bail out from this! */ +#ifdef USE_NGHTTP2 + if(is_http2 && !nread) + DEBUGF(infof(data, "nread == 0, stream closed, bailing\n")); + else +#endif + DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n")); k->keepon &= ~KEEP_RECV; break; } /* Default buffer to use when we write the buffer, it may be changed in the flow below before the actual storing is done. */ - k->str = k->buf; + k->str = buf; if(conn->handler->readwrite) { result = conn->handler->readwrite(data, conn, &nread, &readmore); @@ -675,7 +688,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, /* This is not an 'else if' since it may be a rest from the header parsing, where the beginning of the buffer is headers and the end is non-headers. */ - if(k->str && !k->header && (nread > 0 || is_empty_data)) { + if(!k->header && (nread > 0 || is_empty_data)) { if(data->set.opt_no_body) { /* data arrives although we want none, bail out */ @@ -706,7 +719,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, infof(data, "Ignoring the response-body\n"); } if(data->state.resume_from && !k->content_range && - (data->set.httpreq == HTTPREQ_GET) && + (data->state.httpreq == HTTPREQ_GET) && !k->ignorebody) { if(k->size == data->state.resume_from) { @@ -756,8 +769,9 @@ static CURLcode readwrite_data(struct Curl_easy *data, /* pass data to the debug function before it gets "dechunked" */ if(data->set.verbose) { if(k->badheader) { - Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff, - (size_t)k->hbuflen); + Curl_debug(data, CURLINFO_DATA_IN, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); if(k->badheader == HEADER_PARTHEADER) Curl_debug(data, CURLINFO_DATA_IN, k->str, (size_t)nread); @@ -775,14 +789,14 @@ static CURLcode readwrite_data(struct Curl_easy *data, * and writes away the data. The returned 'nread' holds the number * of actual data it wrote to the client. */ - + CURLcode extra; CHUNKcode res = - Curl_httpchunk_read(conn, k->str, nread, &nread); + Curl_httpchunk_read(conn, k->str, nread, &nread, &extra); if(CHUNKE_OK < res) { - if(CHUNKE_WRITE_ERROR == res) { - failf(data, "Failed writing data"); - return CURLE_WRITE_ERROR; + if(CHUNKE_PASSTHRU_ERROR == res) { + failf(data, "Failed reading the chunked-encoded stream"); + return extra; } failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res)); return CURLE_RECV_ERROR; @@ -808,9 +822,9 @@ static CURLcode readwrite_data(struct Curl_easy *data, /* Account for body content stored in the header buffer */ if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) { - DEBUGF(infof(data, "Increasing bytecount by %zu from hbuflen\n", - k->hbuflen)); - k->bytecount += k->hbuflen; + size_t headlen = Curl_dyn_len(&data->state.headerb); + DEBUGF(infof(data, "Increasing bytecount by %zu\n", headlen)); + k->bytecount += headlen; } if((-1 != k->maxdownload) && @@ -825,6 +839,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, ", maxdownload = %" CURL_FORMAT_CURL_OFF_T ", bytecount = %" CURL_FORMAT_CURL_OFF_T "\n", excess, k->size, k->maxdownload, k->bytecount); + connclose(conn, "excess found in a read"); } nread = (ssize_t) (k->maxdownload - k->bytecount); @@ -844,15 +859,16 @@ static CURLcode readwrite_data(struct Curl_easy *data, if(k->badheader && !k->ignorebody) { /* we parsed a piece of data wrongly assuming it was a header and now we output it as body instead */ + size_t headlen = Curl_dyn_len(&data->state.headerb); /* Don't let excess data pollute body writes */ - if(k->maxdownload == -1 || (curl_off_t)k->hbuflen <= k->maxdownload) + if(k->maxdownload == -1 || (curl_off_t)headlen <= k->maxdownload) result = Curl_client_write(conn, CLIENTWRITE_BODY, - data->state.headerbuff, - k->hbuflen); + Curl_dyn_ptr(&data->state.headerb), + headlen); else result = Curl_client_write(conn, CLIENTWRITE_BODY, - data->state.headerbuff, + Curl_dyn_ptr(&data->state.headerb), (size_t)k->maxdownload); if(result) @@ -886,14 +902,14 @@ static CURLcode readwrite_data(struct Curl_easy *data, } /* if(!header and data to read) */ - if(conn->handler->readwrite && excess && !conn->bits.stream_was_rewound) { + if(conn->handler->readwrite && excess) { /* Parse the excess data */ k->str += nread; - if(&k->str[excess] > &k->buf[data->set.buffer_size]) { + if(&k->str[excess] > &buf[data->set.buffer_size]) { /* the excess amount was too excessive(!), make sure it doesn't read out of buffer */ - excess = &k->buf[data->set.buffer_size] - k->str; + excess = &buf[data->set.buffer_size] - k->str; } nread = (ssize_t)excess; @@ -917,7 +933,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, break; } - } while(data_pending(conn) && maxloops--); + } while(data_pending(data) && maxloops--); if(maxloops <= 0) { /* we mark it as read-again-please */ @@ -937,12 +953,14 @@ static CURLcode readwrite_data(struct Curl_easy *data, return CURLE_OK; } -static CURLcode done_sending(struct connectdata *conn, - struct SingleRequest *k) +CURLcode Curl_done_sending(struct connectdata *conn, + struct SingleRequest *k) { k->keepon &= ~KEEP_SEND; /* we're done writing */ + /* These functions should be moved into the handler struct! */ Curl_http2_done_sending(conn); + Curl_quic_done_sending(conn); if(conn->bits.rewindaftersend) { CURLcode result = Curl_readrewind(conn); @@ -1046,7 +1064,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data, break; } if(nread <= 0) { - result = done_sending(conn, k); + result = Curl_done_sending(conn, k); if(result) return result; break; @@ -1164,14 +1182,14 @@ static CURLcode readwrite_upload(struct Curl_easy *data, k->upload_present = 0; /* no more bytes left */ if(k->upload_done) { - result = done_sending(conn, k); + result = Curl_done_sending(conn, k); if(result) return result; } } - } WHILE_FALSE; /* just to break out from! */ + } while(0); /* just to break out from! */ return CURLE_OK; } @@ -1228,9 +1246,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, /* 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) && - ((select_res & CURL_CSELECT_IN) || conn->bits.stream_was_rewound)) { - + if((k->keepon & KEEP_RECV) && (select_res & CURL_CSELECT_IN)) { result = readwrite_data(data, conn, k, &didwhat, done, comeback); if(result || *done) return result; @@ -1354,20 +1370,14 @@ CURLcode Curl_readwrite(struct connectdata *conn, * in the proper state to have this information available. */ int Curl_single_getsock(const struct connectdata *conn, - curl_socket_t *sock, /* points to numsocks number - of sockets */ - int numsocks) + curl_socket_t *sock) { const struct Curl_easy *data = conn->data; int bitmap = GETSOCK_BLANK; unsigned sockindex = 0; if(conn->handler->perform_getsock) - return conn->handler->perform_getsock(conn, sock, numsocks); - - if(numsocks < 2) - /* simple check but we might need two slots */ - return GETSOCK_BLANK; + return conn->handler->perform_getsock(conn, sock); /* don't include HOLD and PAUSE connections */ if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) { @@ -1439,6 +1449,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) } } + data->state.httpreq = data->set.method; data->change.url = data->set.str[STRING_SET_URL]; /* Init the SSL session ID cache here. We do it here since we want to do it @@ -1458,12 +1469,11 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.authhost.want = data->set.httpauth; data->state.authproxy.want = data->set.proxyauth; Curl_safefree(data->info.wouldredirect); - data->info.wouldredirect = NULL; - if(data->set.httpreq == HTTPREQ_PUT) + if(data->state.httpreq == HTTPREQ_PUT) data->state.infilesize = data->set.filesize; - else if((data->set.httpreq != HTTPREQ_GET) && - (data->set.httpreq != HTTPREQ_HEAD)) { + else if((data->state.httpreq != HTTPREQ_GET) && + (data->state.httpreq != HTTPREQ_HEAD)) { data->state.infilesize = data->set.postfieldsize; if(data->set.postfields && (data->state.infilesize == -1)) data->state.infilesize = (curl_off_t)strlen(data->set.postfields); @@ -1513,6 +1523,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) } } #endif + Curl_http2_init_state(&data->state); } return result; @@ -1594,7 +1605,8 @@ CURLcode Curl_follow(struct Curl_easy *data, DEBUGASSERT(data->state.uh); uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, - (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : 0); + (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : + ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) ); if(uc) { if(type != FOLLOW_FAKE) return Curl_uc_to_curlcode(uc); @@ -1672,12 +1684,12 @@ CURLcode Curl_follow(struct Curl_easy *data, * This behaviour is forbidden by RFC1945 and the obsolete RFC2616, and * can be overridden with CURLOPT_POSTREDIR. */ - if((data->set.httpreq == HTTPREQ_POST - || data->set.httpreq == HTTPREQ_POST_FORM - || data->set.httpreq == HTTPREQ_POST_MIME) + if((data->state.httpreq == HTTPREQ_POST + || data->state.httpreq == HTTPREQ_POST_FORM + || data->state.httpreq == HTTPREQ_POST_MIME) && !(data->set.keep_post & CURL_REDIR_POST_301)) { infof(data, "Switch from POST to GET\n"); - data->set.httpreq = HTTPREQ_GET; + data->state.httpreq = HTTPREQ_GET; } break; case 302: /* Found */ @@ -1697,22 +1709,29 @@ CURLcode Curl_follow(struct Curl_easy *data, * This behaviour is forbidden by RFC1945 and the obsolete RFC2616, and * can be overridden with CURLOPT_POSTREDIR. */ - if((data->set.httpreq == HTTPREQ_POST - || data->set.httpreq == HTTPREQ_POST_FORM - || data->set.httpreq == HTTPREQ_POST_MIME) + if((data->state.httpreq == HTTPREQ_POST + || data->state.httpreq == HTTPREQ_POST_FORM + || data->state.httpreq == HTTPREQ_POST_MIME) && !(data->set.keep_post & CURL_REDIR_POST_302)) { infof(data, "Switch from POST to GET\n"); - data->set.httpreq = HTTPREQ_GET; + data->state.httpreq = HTTPREQ_GET; } break; case 303: /* See Other */ - /* Disable both types of POSTs, unless the user explicitly - asks for POST after POST */ - if(data->set.httpreq != HTTPREQ_GET - && !(data->set.keep_post & CURL_REDIR_POST_303)) { - data->set.httpreq = HTTPREQ_GET; /* enforce GET request */ - infof(data, "Disables POST, goes with %s\n", + /* 'See Other' location is not the resource but a substitute for the + * resource. In this case we switch the method to GET/HEAD, unless the + * method is POST and the user specified to keep it as POST. + * https://github.com/curl/curl/issues/5237#issuecomment-614641049 + */ + if(data->state.httpreq != HTTPREQ_GET && + ((data->state.httpreq != HTTPREQ_POST && + data->state.httpreq != HTTPREQ_POST_FORM && + data->state.httpreq != HTTPREQ_POST_MIME) || + !(data->set.keep_post & CURL_REDIR_POST_303))) { + data->state.httpreq = HTTPREQ_GET; + data->set.upload = false; + infof(data, "Switch to %s\n", data->set.opt_no_body?"HEAD":"GET"); } break; @@ -1779,6 +1798,12 @@ CURLcode Curl_retry_request(struct connectdata *conn, retry = TRUE; } if(retry) { +#define CONN_MAX_RETRIES 5 + if(conn->retrycount++ >= CONN_MAX_RETRIES) { + failf(data, "Connection died, tried %d times before giving up", + CONN_MAX_RETRIES); + return CURLE_SEND_ERROR; + } infof(conn->data, "Connection died, retrying a fresh connect\n"); *url = strdup(conn->data->change.url); if(!*url) @@ -1821,15 +1846,21 @@ Curl_setup_transfer( { struct SingleRequest *k = &data->req; struct connectdata *conn = data->conn; + struct HTTP *http = data->req.protop; + bool httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) && + (http->sending == HTTPSEND_REQUEST)); DEBUGASSERT(conn != NULL); DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); - if(conn->bits.multiplex || conn->httpversion == 20) { + if(conn->bits.multiplex || conn->httpversion == 20 || httpsending) { /* when multiplexing, the read/write sockets need to be the same! */ conn->sockfd = sockindex == -1 ? ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) : conn->sock[sockindex]; conn->writesockfd = conn->sockfd; + if(httpsending) + /* special and very HTTP-specific */ + writesockindex = FIRSTSOCKET; } else { conn->sockfd = sockindex == -1 ? @@ -1857,7 +1888,6 @@ Curl_setup_transfer( k->keepon |= KEEP_RECV; if(writesockindex != -1) { - struct HTTP *http = data->req.protop; /* HTTP 1.1 magic: Even if we require a 100-return code before uploading data, we might diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h index a9bff634867..67fd91f25b4 100644 --- a/Utilities/cmcurl/lib/transfer.h +++ b/Utilities/cmcurl/lib/transfer.h @@ -29,7 +29,6 @@ char *Curl_checkheaders(const struct connectdata *conn, void Curl_init_CONNECT(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); -CURLcode Curl_second_connect(struct connectdata *conn); CURLcode Curl_posttransfer(struct Curl_easy *data); typedef enum { @@ -48,8 +47,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, struct Curl_easy *data, bool *done, bool *comeback); int Curl_single_getsock(const struct connectdata *conn, - curl_socket_t *socks, - int numsocks); + curl_socket_t *socks); CURLcode Curl_readrewind(struct connectdata *conn); CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, size_t *nreadp); @@ -57,6 +55,9 @@ CURLcode Curl_retry_request(struct connectdata *conn, char **url); bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc); CURLcode Curl_get_upload_buffer(struct Curl_easy *data); +CURLcode Curl_done_sending(struct connectdata *conn, + struct SingleRequest *k); + /* This sets up a forthcoming transfer */ void Curl_setup_transfer (struct Curl_easy *data, diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index c441ae7165a..a1a6b691039 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,10 +34,12 @@ #ifdef HAVE_NET_IF_H #include #endif +#ifdef HAVE_IPHLPAPI_H +#include +#endif #ifdef HAVE_SYS_IOCTL_H #include #endif - #ifdef HAVE_SYS_PARAM_H #include #endif @@ -93,6 +95,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "inet_pton.h" #include "getinfo.h" #include "urlapi-int.h" +#include "system_win32.h" /* And now for the protocols */ #include "ftp.h" @@ -103,15 +106,15 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "http2.h" #include "file.h" #include "curl_ldap.h" -#include "ssh.h" +#include "vssh/ssh.h" #include "imap.h" #include "url.h" #include "connect.h" #include "inet_ntop.h" #include "http_ntlm.h" -#include "socks.h" #include "curl_rtmp.h" #include "gopher.h" +#include "mqtt.h" #include "http_proxy.h" #include "conncache.h" #include "multihandle.h" @@ -119,6 +122,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "strdup.h" #include "setopt.h" #include "altsvc.h" +#include "dynbuf.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -126,7 +130,6 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "memdebug.h" static void conn_free(struct connectdata *conn); -static void free_idnconverted_hostname(struct hostname *host); static unsigned int get_protocol_family(unsigned int protocol); /* Some parts of the code (e.g. chunked encoding) assume this buffer has at @@ -139,19 +142,21 @@ static unsigned int get_protocol_family(unsigned int protocol); /* - * Protocol table. + * Protocol table. Schemes (roughly) in 2019 popularity order: + * + * HTTPS, HTTP, FTP, FTPS, SFTP, FILE, SCP, SMTP, LDAP, IMAPS, TELNET, IMAP, + * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT */ - static const struct Curl_handler * const protocols[] = { -#ifndef CURL_DISABLE_HTTP - &Curl_handler_http, -#endif - #if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) &Curl_handler_https, #endif +#ifndef CURL_DISABLE_HTTP + &Curl_handler_http, +#endif + #ifndef CURL_DISABLE_FTP &Curl_handler_ftp, #endif @@ -160,12 +165,23 @@ static const struct Curl_handler * const protocols[] = { &Curl_handler_ftps, #endif -#ifndef CURL_DISABLE_TELNET - &Curl_handler_telnet, +#if defined(USE_SSH) + &Curl_handler_sftp, #endif -#ifndef CURL_DISABLE_DICT - &Curl_handler_dict, +#ifndef CURL_DISABLE_FILE + &Curl_handler_file, +#endif + +#if defined(USE_SSH) && !defined(USE_WOLFSSH) + &Curl_handler_scp, +#endif + +#ifndef CURL_DISABLE_SMTP + &Curl_handler_smtp, +#ifdef USE_SSL + &Curl_handler_smtps, +#endif #endif #ifndef CURL_DISABLE_LDAP @@ -177,22 +193,6 @@ static const struct Curl_handler * const protocols[] = { #endif #endif -#ifndef CURL_DISABLE_FILE - &Curl_handler_file, -#endif - -#ifndef CURL_DISABLE_TFTP - &Curl_handler_tftp, -#endif - -#if defined(USE_SSH) - &Curl_handler_scp, -#endif - -#if defined(USE_SSH) - &Curl_handler_sftp, -#endif - #ifndef CURL_DISABLE_IMAP &Curl_handler_imap, #ifdef USE_SSL @@ -200,6 +200,14 @@ static const struct Curl_handler * const protocols[] = { #endif #endif +#ifndef CURL_DISABLE_TELNET + &Curl_handler_telnet, +#endif + +#ifndef CURL_DISABLE_TFTP + &Curl_handler_tftp, +#endif + #ifndef CURL_DISABLE_POP3 &Curl_handler_pop3, #ifdef USE_SSL @@ -216,17 +224,14 @@ static const struct Curl_handler * const protocols[] = { #endif #endif -#ifndef CURL_DISABLE_SMTP - &Curl_handler_smtp, -#ifdef USE_SSL - &Curl_handler_smtps, -#endif -#endif - #ifndef CURL_DISABLE_RTSP &Curl_handler_rtsp, #endif +#ifdef CURL_ENABLE_MQTT + &Curl_handler_mqtt, +#endif + #ifndef CURL_DISABLE_GOPHER &Curl_handler_gopher, #endif @@ -240,6 +245,10 @@ static const struct Curl_handler * const protocols[] = { &Curl_handler_rtmpts, #endif +#ifndef CURL_DISABLE_DICT + &Curl_handler_dict, +#endif + (struct Curl_handler *) NULL }; @@ -272,10 +281,16 @@ void Curl_freeset(struct Curl_easy *data) { /* Free all dynamic strings stored in the data->set substructure. */ enum dupstring i; + enum dupblob j; + for(i = (enum dupstring)0; i < STRING_LAST; i++) { Curl_safefree(data->set.str[i]); } + for(j = (enum dupblob)0; j < BLOB_LAST; j++) { + Curl_safefree(data->set.blobs[j]); + } + if(data->change.referer_alloc) { Curl_safefree(data->change.referer); data->change.referer_alloc = FALSE; @@ -315,13 +330,17 @@ static void up_free(struct Curl_easy *data) * when curl_easy_perform() is invoked. */ -CURLcode Curl_close(struct Curl_easy *data) +CURLcode Curl_close(struct Curl_easy **datap) { struct Curl_multi *m; + struct Curl_easy *data; - if(!data) + if(!datap || !*datap) return CURLE_OK; + data = *datap; + *datap = NULL; + Curl_expire_clear(data); /* shut off timers */ m = data->multi; @@ -370,11 +389,11 @@ CURLcode Curl_close(struct Curl_easy *data) up_free(data); Curl_safefree(data->state.buffer); - Curl_safefree(data->state.headerbuff); + Curl_dyn_free(&data->state.headerb); Curl_safefree(data->state.ulbuf); - Curl_flush_cookies(data, 1); + Curl_flush_cookies(data, TRUE); #ifdef USE_ALTSVC - Curl_altsvc_save(data->asi, data->set.str[STRING_ALTSVC]); + Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); Curl_altsvc_cleanup(data->asi); data->asi = NULL; #endif @@ -397,6 +416,23 @@ CURLcode Curl_close(struct Curl_easy *data) Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); } + Curl_safefree(data->state.aptr.proxyuserpwd); + Curl_safefree(data->state.aptr.uagent); + Curl_safefree(data->state.aptr.userpwd); + Curl_safefree(data->state.aptr.accept_encoding); + Curl_safefree(data->state.aptr.te); + Curl_safefree(data->state.aptr.rangeline); + Curl_safefree(data->state.aptr.ref); + Curl_safefree(data->state.aptr.host); + Curl_safefree(data->state.aptr.cookiehost); + Curl_safefree(data->state.aptr.rtsp_transport); + +#ifndef CURL_DISABLE_DOH + Curl_dyn_free(&data->req.doh.probe[0].serverdoh); + Curl_dyn_free(&data->req.doh.probe[1].serverdoh); + curl_slist_free_all(data->req.doh.headers); +#endif + /* destruct wildcard structures if it is needed */ Curl_wildcard_dtor(&data->wildcard); Curl_freeset(data); @@ -437,9 +473,9 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->postfieldsize = -1; /* unknown size */ set->maxredirs = -1; /* allow any amount by default */ - set->httpreq = HTTPREQ_GET; /* Default HTTP request */ + set->method = HTTPREQ_GET; /* Default HTTP request */ set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */ -#ifndef CURL_DISABLE_FILE +#ifndef CURL_DISABLE_FTP set->ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */ @@ -476,7 +512,9 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) type */ set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by default */ +#ifndef CURL_DISABLE_PROXY set->proxy_ssl = set->ssl; +#endif set->new_file_perms = 0644; /* Default permissions */ set->new_directory_perms = 0755; /* Default permissions */ @@ -485,9 +523,8 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) define since we internally only use the lower 16 bits for the passed in bitmask to not conflict with the private bits */ set->allowed_protocols = CURLPROTO_ALL; - set->redir_protocols = CURLPROTO_ALL & /* All except FILE, SCP and SMB */ - ~(CURLPROTO_FILE | CURLPROTO_SCP | CURLPROTO_SMB | - CURLPROTO_SMBS); + set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | + CURLPROTO_FTPS; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* @@ -544,7 +581,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ set->maxage_conn = 118; - set->http09_allowed = TRUE; + set->http09_allowed = FALSE; set->httpversion = #ifdef USE_NGHTTP2 CURL_HTTP_VERSION_2TLS @@ -586,40 +623,22 @@ CURLcode Curl_open(struct Curl_easy **curl) return result; } - /* We do some initial setup here, all those fields that can't be just 0 */ - - data->state.buffer = malloc(READBUFFER_SIZE + 1); - if(!data->state.buffer) { - DEBUGF(fprintf(stderr, "Error: malloc of buffer failed\n")); - result = CURLE_OUT_OF_MEMORY; - } - else { - data->state.headerbuff = malloc(HEADERSIZE); - if(!data->state.headerbuff) { - DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n")); - result = CURLE_OUT_OF_MEMORY; - } - else { - result = Curl_init_userdefined(data); - - data->state.headersize = HEADERSIZE; - Curl_convert_init(data); - Curl_initinfo(data); - - /* most recent connection is not yet defined */ - data->state.lastconnect = NULL; + result = Curl_init_userdefined(data); + if(!result) { + Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER); + Curl_convert_init(data); + Curl_initinfo(data); - data->progress.flags |= PGRS_HIDE; - data->state.current_speed = -1; /* init to negative == impossible */ + /* most recent connection is not yet defined */ + data->state.lastconnect = NULL; - Curl_http2_init_state(&data->state); - } + data->progress.flags |= PGRS_HIDE; + data->state.current_speed = -1; /* init to negative == impossible */ } if(result) { Curl_resolver_cleanup(data->state.resolver); - free(data->state.buffer); - free(data->state.headerbuff); + Curl_dyn_free(&data->state.headerb); Curl_freeset(data); free(data); data = NULL; @@ -665,15 +684,13 @@ static void conn_reset_all_postponed_data(struct connectdata *conn) } #else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ /* Use "do-nothing" macro instead of function when workaround not used */ -#define conn_reset_all_postponed_data(c) do {} WHILE_FALSE +#define conn_reset_all_postponed_data(c) do {} while(0) #endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ static void conn_shutdown(struct connectdata *conn) { - if(!conn) - return; - + DEBUGASSERT(conn); infof(conn->data, "Closing connection %ld\n", conn->connection_id); DEBUGASSERT(conn->data); @@ -694,54 +711,40 @@ static void conn_shutdown(struct connectdata *conn) Curl_closesocket(conn, conn->tempsock[0]); if(CURL_SOCKET_BAD != conn->tempsock[1]) Curl_closesocket(conn, conn->tempsock[1]); - - /* unlink ourselves. this should be called last since other shutdown - procedures need a valid conn->data and this may clear it. */ - Curl_conncache_remove_conn(conn->data, conn, TRUE); } static void conn_free(struct connectdata *conn) { - if(!conn) - return; - - free_idnconverted_hostname(&conn->host); - free_idnconverted_hostname(&conn->conn_to_host); - free_idnconverted_hostname(&conn->http_proxy.host); - free_idnconverted_hostname(&conn->socks_proxy.host); + DEBUGASSERT(conn); - Curl_safefree(conn->user); - Curl_safefree(conn->passwd); - Curl_safefree(conn->oauth_bearer); - Curl_safefree(conn->options); + Curl_free_idnconverted_hostname(&conn->host); + Curl_free_idnconverted_hostname(&conn->conn_to_host); +#ifndef CURL_DISABLE_PROXY + Curl_free_idnconverted_hostname(&conn->http_proxy.host); + Curl_free_idnconverted_hostname(&conn->socks_proxy.host); 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->allocptr.proxyuserpwd); - Curl_safefree(conn->allocptr.uagent); - Curl_safefree(conn->allocptr.userpwd); - Curl_safefree(conn->allocptr.accept_encoding); - Curl_safefree(conn->allocptr.te); - Curl_safefree(conn->allocptr.rangeline); - Curl_safefree(conn->allocptr.ref); - Curl_safefree(conn->allocptr.host); - Curl_safefree(conn->allocptr.cookiehost); - Curl_safefree(conn->allocptr.rtsp_transport); - Curl_safefree(conn->trailer); + Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ + Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ + Curl_free_primary_ssl_config(&conn->proxy_ssl_config); +#endif + Curl_safefree(conn->user); + Curl_safefree(conn->passwd); + Curl_safefree(conn->sasl_authzid); + Curl_safefree(conn->options); + Curl_dyn_free(&conn->trailer); Curl_safefree(conn->host.rawalloc); /* host name buffer */ Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ Curl_safefree(conn->hostname_resolve); Curl_safefree(conn->secondaryhostname); - Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ - Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ Curl_safefree(conn->connect_state); conn_reset_all_postponed_data(conn); Curl_llist_destroy(&conn->easyq, NULL); Curl_safefree(conn->localdev); Curl_free_primary_ssl_config(&conn->ssl_config); - Curl_free_primary_ssl_config(&conn->proxy_ssl_config); #ifdef USE_UNIX_SOCKETS Curl_safefree(conn->unix_domain_socket); @@ -770,13 +773,17 @@ static void conn_free(struct connectdata *conn) CURLcode Curl_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead_connection) { - if(!conn) - return CURLE_OK; /* this is closed and fine already */ + /* there must be a connection to close */ + DEBUGASSERT(conn); - if(!data) { - DEBUGF(infof(data, "DISCONNECT without easy handle, ignoring\n")); - return CURLE_OK; - } + /* it must be removed from the connection cache */ + DEBUGASSERT(!conn->bundle); + + /* there must be an associated transfer */ + DEBUGASSERT(data); + + /* the transfer must be detached from the connection */ + DEBUGASSERT(!data->conn); /* * If this connection isn't marked to force-close, leave it open if there @@ -792,16 +799,11 @@ CURLcode Curl_disconnect(struct Curl_easy *data, conn->dns_entry = NULL; } - Curl_hostcache_prune(data); /* kill old DNS cache entries */ - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) /* Cleanup NTLM connection-related data */ Curl_http_auth_cleanup_ntlm(conn); -#endif -#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) + /* Cleanup NEGOTIATE connection-related data */ Curl_http_auth_cleanup_negotiate(conn); -#endif /* the protocol specific disconnect handler and conn_shutdown need a transfer for the connection! */ @@ -863,8 +865,8 @@ static int IsMultiplexingPossible(const struct Curl_easy *handle, #ifndef CURL_DISABLE_PROXY static bool -proxy_info_matches(const struct proxy_info* data, - const struct proxy_info* needle) +proxy_info_matches(const struct proxy_info *data, + const struct proxy_info *needle) { if((data->proxytype == needle->proxytype) && (data->port == needle->port) && @@ -873,11 +875,59 @@ proxy_info_matches(const struct proxy_info* data, return FALSE; } + +static bool +socks_proxy_info_matches(const struct proxy_info *data, + const struct proxy_info *needle) +{ + if(!proxy_info_matches(data, needle)) + return FALSE; + + /* the user information is case-sensitive + or at least it is not defined as case-insensitive + see https://tools.ietf.org/html/rfc3986#section-3.2.1 */ + if((data->user == NULL) != (needle->user == NULL)) + return FALSE; + /* curl_strequal does a case insentive comparison, so do not use it here! */ + if(data->user && + needle->user && + strcmp(data->user, needle->user) != 0) + return FALSE; + if((data->passwd == NULL) != (needle->passwd == NULL)) + return FALSE; + /* curl_strequal does a case insentive comparison, so do not use it here! */ + if(data->passwd && + needle->passwd && + strcmp(data->passwd, needle->passwd) != 0) + return FALSE; + return TRUE; +} #else /* disabled, won't get called */ #define proxy_info_matches(x,y) FALSE +#define socks_proxy_info_matches(x,y) FALSE #endif +/* A connection has to have been idle for a shorter time than 'maxage_conn' to + be subject for reuse. The success rate is just too low after this. */ + +static bool conn_maxage(struct Curl_easy *data, + struct connectdata *conn, + struct curltime now) +{ + if(!conn->data) { + timediff_t idletime = Curl_timediff(now, conn->lastused); + idletime /= 1000; /* integer seconds is fine */ + + if(idletime > data->set.maxage_conn) { + infof(data, "Too old connection (%ld seconds), disconnect it\n", + idletime); + return TRUE; + } + } + return FALSE; +} + /* * This function checks if the given connection is dead and extracts it from * the connection cache if so. @@ -894,7 +944,11 @@ static bool extract_if_dead(struct connectdata *conn, /* The check for a dead socket makes sense only if the connection isn't in use */ bool dead; - if(conn->handler->connection_check) { + struct curltime now = Curl_now(); + if(conn_maxage(data, conn, now)) { + dead = TRUE; + } + else if(conn->handler->connection_check) { /* 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; @@ -946,7 +1000,12 @@ static int call_extract_if_dead(struct connectdata *conn, void *param) static void prune_dead_connections(struct Curl_easy *data) { struct curltime now = Curl_now(); - time_t elapsed = Curl_timediff(now, data->state.conn_cache->last_cleanup); + timediff_t elapsed; + + CONNCACHE_LOCK(data); + elapsed = + Curl_timediff(now, data->state.conn_cache->last_cleanup); + CONNCACHE_UNLOCK(data); if(elapsed >= 1000L) { struct prunedead prune; @@ -954,32 +1013,20 @@ static void prune_dead_connections(struct Curl_easy *data) prune.extracted = NULL; while(Curl_conncache_foreach(data, data->state.conn_cache, &prune, call_extract_if_dead)) { + /* unlocked */ + + /* remove connection from cache */ + Curl_conncache_remove_conn(data, prune.extracted, TRUE); + /* disconnect it */ (void)Curl_disconnect(data, prune.extracted, /* dead_connection */TRUE); } + CONNCACHE_LOCK(data); data->state.conn_cache->last_cleanup = now; + CONNCACHE_UNLOCK(data); } } -/* A connection has to have been idle for a shorter time than 'maxage_conn' to - be subject for reuse. The success rate is just too low after this. */ - -static bool conn_maxage(struct Curl_easy *data, - struct connectdata *conn, - struct curltime now) -{ - if(!conn->data) { - timediff_t idletime = Curl_timediff(now, conn->lastused); - idletime /= 1000; /* integer seconds is fine */ - - if(idletime/1000 > data->set.maxage_conn) { - infof(data, "Too old connection (%ld seconds), disconnect it\n", - idletime); - return TRUE; - } - } - return FALSE; -} /* * Given one filled in connection struct (named needle), this function should * detect if there already is one that has all the significant details @@ -1003,16 +1050,20 @@ ConnectionExists(struct Curl_easy *data, bool foundPendingCandidate = FALSE; bool canmultiplex = IsMultiplexingPossible(data, needle); struct connectbundle *bundle; - struct curltime now = Curl_now(); + const char *hostbundle; #ifdef USE_NTLM bool wantNTLMhttp = ((data->state.authhost.want & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && (needle->handler->protocol & PROTO_FAMILY_HTTP)); +#ifndef CURL_DISABLE_PROXY bool wantProxyNTLMhttp = (needle->bits.proxy_user_passwd && ((data->state.authproxy.want & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && (needle->handler->protocol & PROTO_FAMILY_HTTP))); +#else + bool wantProxyNTLMhttp = FALSE; +#endif #endif *force_reuse = FALSE; @@ -1020,24 +1071,23 @@ ConnectionExists(struct Curl_easy *data, /* Look up the bundle with all the connections to this particular host. Locks the connection cache, beware of early returns! */ - bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache); + bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache, + &hostbundle); if(bundle) { /* Max pipe length is zero (unlimited) for multiplexed connections */ struct curl_llist_element *curr; infof(data, "Found bundle for host %s: %p [%s]\n", - (needle->bits.conn_to_host ? needle->conn_to_host.name : - needle->host.name), (void *)bundle, - (bundle->multiuse == BUNDLE_MULTIPLEX ? - "can multiplex" : "serially")); + hostbundle, (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? + "can multiplex" : "serially")); /* We can't multiplex if we don't know anything about the server */ if(canmultiplex) { if(bundle->multiuse == BUNDLE_UNKNOWN) { - if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) { + if(data->set.pipewait) { infof(data, "Server doesn't support multiplex yet, wait\n"); *waitpipe = TRUE; - Curl_conncache_unlock(data); + CONNCACHE_UNLOCK(data); return FALSE; /* no re-use */ } @@ -1058,7 +1108,7 @@ ConnectionExists(struct Curl_easy *data, curr = bundle->conn_list.head; while(curr) { bool match = FALSE; - size_t multiplexed; + size_t multiplexed = 0; /* * Note that if we use a HTTP proxy in normal mode (no tunneling), we @@ -1067,22 +1117,15 @@ ConnectionExists(struct Curl_easy *data, check = curr->ptr; curr = curr->next; - if(check->bits.connect_only) - /* connect-only connections will not be reused */ + if(check->bits.connect_only || check->bits.close) + /* connect-only or to-be-closed connections will not be reused */ continue; - if(conn_maxage(data, check, now) || extract_if_dead(check, data)) { - /* disconnect it */ - (void)Curl_disconnect(data, check, /* dead_connection */TRUE); - continue; - } - - multiplexed = CONN_INUSE(check) && - (bundle->multiuse == BUNDLE_MULTIPLEX); + if(bundle->multiuse == BUNDLE_MULTIPLEX) + multiplexed = CONN_INUSE(check); if(canmultiplex) { - if(check->bits.protoconnstart && check->bits.close) - continue; + ; } else { if(multiplexed) { @@ -1102,12 +1145,9 @@ ConnectionExists(struct Curl_easy *data, } } - if((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || - check->bits.close) { - if(!check->bits.close) - foundPendingCandidate = TRUE; - /* Don't pick a connection that hasn't connected yet or that is going - to get closed. */ + if(check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) { + foundPendingCandidate = TRUE; + /* Don't pick a connection that hasn't connected yet */ infof(data, "Connection #%ld isn't open enough, can't reuse\n", check->connection_id); continue; @@ -1120,7 +1160,8 @@ ConnectionExists(struct Curl_easy *data, continue; if(strcmp(needle->unix_domain_socket, check->unix_domain_socket)) continue; - if(needle->abstract_unix_socket != check->abstract_unix_socket) + if(needle->bits.abstract_unix_socket != + check->bits.abstract_unix_socket) continue; } else if(check->unix_domain_socket) @@ -1131,18 +1172,20 @@ ConnectionExists(struct Curl_easy *data, (check->handler->flags&PROTOPT_SSL)) /* don't do mixed SSL and non-SSL connections */ if(get_protocol_family(check->handler->protocol) != - needle->handler->protocol || !check->tls_upgraded) + needle->handler->protocol || !check->bits.tls_upgraded) /* except protocols that have been upgraded via TLS */ continue; +#ifndef CURL_DISABLE_PROXY if(needle->bits.httpproxy != check->bits.httpproxy || needle->bits.socksproxy != check->bits.socksproxy) continue; - if(needle->bits.socksproxy && !proxy_info_matches(&needle->socks_proxy, - &check->socks_proxy)) + if(needle->bits.socksproxy && + !socks_proxy_info_matches(&needle->socks_proxy, + &check->socks_proxy)) continue; - +#endif if(needle->bits.conn_to_host != check->bits.conn_to_host) /* don't mix connections that use the "connect to host" feature and * connections that don't use this feature */ @@ -1153,6 +1196,7 @@ ConnectionExists(struct Curl_easy *data, * connections that don't use this feature */ continue; +#ifndef CURL_DISABLE_PROXY if(needle->bits.httpproxy) { if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy)) continue; @@ -1179,14 +1223,16 @@ ConnectionExists(struct Curl_easy *data, } } } +#endif + + DEBUGASSERT(!check->data || GOOD_EASY_HANDLE(check->data)); if(!canmultiplex && check->data) /* this request can't be multiplexed but the checked connection is already in use so we skip it */ continue; - if(CONN_INUSE(check) && check->data && - (check->data->multi != needle->data->multi)) + if(check->data && (check->data->multi != needle->data->multi)) /* this could be subject for multiplex use, but only if they belong to * the same multi handle */ continue; @@ -1220,22 +1266,25 @@ ConnectionExists(struct Curl_easy *data, } } - if(!needle->bits.httpproxy || (needle->handler->flags&PROTOPT_SSL) || - needle->bits.tunnel_proxy) { + if((needle->handler->flags&PROTOPT_SSL) +#ifndef CURL_DISABLE_PROXY + || !needle->bits.httpproxy || needle->bits.tunnel_proxy +#endif + ) { /* The requested connection does not use a HTTP proxy or it uses SSL or it is a non-SSL protocol tunneled or it is a non-SSL protocol which is allowed to be upgraded via TLS */ if((strcasecompare(needle->handler->scheme, check->handler->scheme) || (get_protocol_family(check->handler->protocol) == - needle->handler->protocol && check->tls_upgraded)) && + needle->handler->protocol && check->bits.tls_upgraded)) && (!needle->bits.conn_to_host || strcasecompare( needle->conn_to_host.name, check->conn_to_host.name)) && (!needle->bits.conn_to_port || needle->conn_to_port == check->conn_to_port) && strcasecompare(needle->host.name, check->host.name) && needle->remote_port == check->remote_port) { - /* The schemes match or the the protocol family is the same and the + /* The schemes match or the protocol family is the same and the previous connection was TLS upgraded, and the hostname and host port match */ if(needle->handler->flags & PROTOPT_SSL) { @@ -1276,14 +1325,21 @@ ConnectionExists(struct Curl_easy *data, partway through a handshake!) */ if(wantNTLMhttp) { if(strcmp(needle->user, check->user) || - strcmp(needle->passwd, check->passwd)) + strcmp(needle->passwd, check->passwd)) { + + /* we prefer a credential match, but this is at least a connection + that can be reused and "upgraded" to NTLM */ + if(check->http_ntlm_state == NTLMSTATE_NONE) + chosen = check; continue; + } } else if(check->http_ntlm_state != NTLMSTATE_NONE) { /* Connection is using NTLM auth but we don't want NTLM */ continue; } +#ifndef CURL_DISABLE_PROXY /* Same for Proxy NTLM authentication */ if(wantProxyNTLMhttp) { /* Both check->http_proxy.user and check->http_proxy.passwd can be @@ -1299,7 +1355,7 @@ ConnectionExists(struct Curl_easy *data, /* Proxy connection is using NTLM auth but we don't want NTLM */ continue; } - +#endif if(wantNTLMhttp || wantProxyNTLMhttp) { /* Credentials are already checked, we can use this connection */ chosen = check; @@ -1337,6 +1393,13 @@ ConnectionExists(struct Curl_easy *data, multiplexed); continue; } + else if(multiplexed >= + Curl_multi_max_concurrent_streams(needle->data->multi)) { + infof(data, "client side MAX_CONCURRENT_STREAMS reached" + ", skip (%zu)\n", + multiplexed); + continue; + } } #endif /* When not multiplexed, we have a match here! */ @@ -1356,11 +1419,12 @@ ConnectionExists(struct Curl_easy *data, if(chosen) { /* mark it as used before releasing the lock */ chosen->data = data; /* own it! */ - Curl_conncache_unlock(data); + Curl_attach_connnection(data, chosen); + CONNCACHE_UNLOCK(data); *usethis = chosen; return TRUE; /* yes, we found one to use! */ } - Curl_conncache_unlock(data); + CONNCACHE_UNLOCK(data); if(foundPendingCandidate && data->set.pipewait) { infof(data, @@ -1371,58 +1435,6 @@ ConnectionExists(struct Curl_easy *data, return FALSE; /* no matching connecting exists */ } -/* after a TCP connection to the proxy has been verified, this function does - the next magic step. - - Note: this function's sub-functions call failf() - -*/ -CURLcode Curl_connected_proxy(struct connectdata *conn, int sockindex) -{ - CURLcode result = CURLE_OK; - - if(conn->bits.socksproxy) { -#ifndef CURL_DISABLE_PROXY - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - const char * const host = 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; - const int 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; - conn->bits.socksproxy_connecting = TRUE; - switch(conn->socks_proxy.proxytype) { - case CURLPROXY_SOCKS5: - case CURLPROXY_SOCKS5_HOSTNAME: - result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd, - host, port, sockindex, conn); - break; - - case CURLPROXY_SOCKS4: - case CURLPROXY_SOCKS4A: - result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, - conn); - break; - - default: - failf(conn->data, "unknown proxytype option given"); - result = CURLE_COULDNT_CONNECT; - } /* switch proxytype */ - conn->bits.socksproxy_connecting = FALSE; -#else - (void)sockindex; -#endif /* CURL_DISABLE_PROXY */ - } - - return result; -} - /* * verboseconnect() displays verbose information after a connect */ @@ -1431,142 +1443,27 @@ void Curl_verboseconnect(struct connectdata *conn) { if(conn->data->set.verbose) infof(conn->data, "Connected to %s (%s) port %ld (#%ld)\n", +#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, conn->ip_addr_str, conn->port, conn->connection_id); } #endif -int Curl_protocol_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) -{ - if(conn->handler->proto_getsock) - return conn->handler->proto_getsock(conn, socks, numsocks); - /* Backup getsock logic. Since there is a live socket in use, we must wait - for it or it will be removed from watching when the multi_socket API is - used. */ - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); -} - -int Curl_doing_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) -{ - if(conn && conn->handler->doing_getsock) - return conn->handler->doing_getsock(conn, socks, numsocks); - return GETSOCK_BLANK; -} - -/* - * We are doing protocol-specific connecting and this is being called over and - * over from the multi interface until the connection phase is done on - * protocol layer. - */ - -CURLcode Curl_protocol_connecting(struct connectdata *conn, - bool *done) -{ - CURLcode result = CURLE_OK; - - if(conn && conn->handler->connecting) { - *done = FALSE; - result = conn->handler->connecting(conn, done); - } - else - *done = TRUE; - - return result; -} - -/* - * We are DOING this is being called over and over from the multi interface - * until the DOING phase is done on protocol layer. - */ - -CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done) -{ - CURLcode result = CURLE_OK; - - if(conn && conn->handler->doing) { - *done = FALSE; - result = conn->handler->doing(conn, done); - } - else - *done = TRUE; - - return result; -} - -/* - * We have discovered that the TCP connection has been successful, we can now - * proceed with some action. - * - */ -CURLcode Curl_protocol_connect(struct connectdata *conn, - bool *protocol_done) -{ - CURLcode result = CURLE_OK; - - *protocol_done = FALSE; - - if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) { - /* We already are connected, get back. This may happen when the connect - worked fine in the first call, like when we connect to a local server - or proxy. Note that we don't know if the protocol is actually done. - - Unless this protocol doesn't have any protocol-connect callback, as - then we know we're done. */ - if(!conn->handler->connecting) - *protocol_done = TRUE; - - return CURLE_OK; - } - - if(!conn->bits.protoconnstart) { - - result = Curl_proxy_connect(conn, FIRSTSOCKET); - if(result) - return result; - - if(CONNECT_FIRSTSOCKET_PROXY_SSL()) - /* wait for HTTPS proxy SSL initialization to complete */ - return CURLE_OK; - - if(conn->bits.tunnel_proxy && conn->bits.httpproxy && - Curl_connect_ongoing(conn)) - /* when using an HTTP tunnel proxy, await complete tunnel establishment - before proceeding further. Return CURLE_OK so we'll be called again */ - return CURLE_OK; - - if(conn->handler->connect_it) { - /* is there a protocol-specific connect() procedure? */ - - /* Call the protocol-specific connect function */ - result = conn->handler->connect_it(conn, protocol_done); - } - else - *protocol_done = TRUE; - - /* it has started, possibly even completed but that knowledge isn't stored - in this bit! */ - if(!result) - conn->bits.protoconnstart = TRUE; - } - - return result; /* pass back status */ -} - /* * Helpers for IDNA conversions. */ -static bool is_ASCII_name(const char *hostname) +bool Curl_is_ASCII_name(const char *hostname) { + /* get an UNSIGNED local version of the pointer */ const unsigned char *ch = (const unsigned char *)hostname; + if(!hostname) /* bad input, consider it ASCII! */ + return TRUE; + while(*ch) { if(*ch++ & 0x80) return FALSE; @@ -1591,8 +1488,8 @@ static void strip_trailing_dot(struct hostname *host) /* * Perform any necessary IDN conversion of hostname */ -static CURLcode idnconvert_hostname(struct connectdata *conn, - struct hostname *host) +CURLcode Curl_idnconvert_hostname(struct connectdata *conn, + struct hostname *host) { struct Curl_easy *data = conn->data; @@ -1607,7 +1504,7 @@ static CURLcode idnconvert_hostname(struct connectdata *conn, host->dispname = host->name; /* Check name for non-ASCII and convert hostname to ACE form if we can */ - if(!is_ASCII_name(host->name)) { + if(!Curl_is_ASCII_name(host->name)) { #ifdef USE_LIBIDN2 if(idn2_check_version(IDN2_VERSION)) { char *ace_hostname = NULL; @@ -1640,7 +1537,9 @@ static CURLcode idnconvert_hostname(struct connectdata *conn, host->name = host->encalloc; } else { - failf(data, "Failed to convert %s to ACE;\n", host->name); + char buffer[STRERROR_LEN]; + failf(data, "Failed to convert %s to ACE; %s\n", host->name, + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); return CURLE_URL_MALFORMAT; } #else @@ -1653,7 +1552,7 @@ static CURLcode idnconvert_hostname(struct connectdata *conn, /* * Frees data allocated by idnconvert_hostname() */ -static void free_idnconverted_hostname(struct hostname *host) +void Curl_free_idnconverted_hostname(struct hostname *host) { #if defined(USE_LIBIDN2) if(host->encalloc) { @@ -1670,13 +1569,6 @@ static void free_idnconverted_hostname(struct hostname *host) #endif } -static void llist_dtor(void *user, void *element) -{ - (void)user; - (void)element; - /* Do nothing */ -} - /* * Allocate and initialize a new connectdata object. */ @@ -1701,8 +1593,10 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->ssl_extra = ssl; conn->ssl[0].backend = (void *)ssl; conn->ssl[1].backend = (void *)(ssl + sslsize); +#ifndef CURL_DISABLE_PROXY conn->proxy_ssl[0].backend = (void *)(ssl + 2 * sslsize); conn->proxy_ssl[1].backend = (void *)(ssl + 3 * sslsize); +#endif } #endif @@ -1741,10 +1635,10 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->data = data; /* Setup the association between this connection and the Curl_easy */ +#ifndef CURL_DISABLE_PROXY conn->http_proxy.proxytype = data->set.proxytype; conn->socks_proxy.proxytype = CURLPROXY_SOCKS4; -#if !defined(CURL_DISABLE_PROXY) /* note that these two proxy bits are now just on what looks to be requested, they may be altered down the road */ conn->bits.proxy = (data->set.str[STRING_PROXY] && @@ -1775,20 +1669,24 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->ssl_config.verifystatus = data->set.ssl.primary.verifystatus; conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer; conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost; +#ifndef CURL_DISABLE_PROXY conn->proxy_ssl_config.verifystatus = data->set.proxy_ssl.primary.verifystatus; conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer; conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost; +#endif conn->ip_version = data->set.ipver; conn->bits.connect_only = data->set.connect_only; + conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */ #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ defined(NTLM_WB_ENABLED) - conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; + conn->ntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; + conn->proxyntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; #endif /* Initialize the easy handle list */ - Curl_llist_init(&conn->easyq, (curl_llist_dtor) llist_dtor); + Curl_llist_init(&conn->easyq, NULL); #ifdef HAVE_GSSAPI conn->data_prot = PROT_CLEAR; @@ -1807,6 +1705,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) it may live on without (this specific) Curl_easy */ conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; + conn->lastused = Curl_now(); /* used now */ return conn; error: @@ -1883,6 +1782,50 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc) } } +/* + * If the URL was set with an IPv6 numerical address with a zone id part, set + * the scope_id based on that! + */ + +static void zonefrom_url(CURLU *uh, struct connectdata *conn) +{ + char *zoneid; + CURLUcode uc; + + uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); + + if(!uc && zoneid) { + char *endp; + unsigned long scope = strtoul(zoneid, &endp, 10); + if(!*endp && (scope < UINT_MAX)) + /* A plain number, use it directly as a scope id. */ + conn->scope_id = (unsigned int)scope; +#if defined(HAVE_IF_NAMETOINDEX) + else { +#elif defined(WIN32) + else if(Curl_if_nametoindex) { +#endif + +#if defined(HAVE_IF_NAMETOINDEX) || defined(WIN32) + /* Zone identifier is not numeric */ + unsigned int scopeidx = 0; +#if defined(WIN32) + scopeidx = Curl_if_nametoindex(zoneid); +#else + scopeidx = if_nametoindex(zoneid); +#endif + if(!scopeidx) + infof(conn->data, "Invalid zoneid: %s; %s\n", zoneid, + strerror(errno)); + else + conn->scope_id = scopeidx; + } +#endif /* HAVE_IF_NAMETOINDEX || WIN32 */ + + free(zoneid); + } +} + /* * Parse URL and fill in the relevant members of the connection struct. */ @@ -1921,6 +1864,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, } if(!data->set.uh) { + char *newurl; uc = curl_url_set(uh, CURLUPART_URL, data->change.url, CURLU_GUESS_SCHEME | CURLU_NON_SUPPORT_SCHEME | @@ -1931,6 +1875,15 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, DEBUGF(infof(data, "curl_url_set rejected %s\n", data->change.url)); return Curl_uc_to_curlcode(uc); } + + /* after it was parsed, get the generated normalized version */ + uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + if(data->change.url_alloc) + free(data->change.url); + data->change.url = newurl; + data->change.url_alloc = TRUE; } uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); @@ -1941,23 +1894,32 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, if(result) return result; - uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, - CURLU_URLDECODE); + /* we don't use the URL API's URL decoder option here since it rejects + control codes and we want to allow them for some schemes in the user and + password fields */ + uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); if(!uc) { - conn->user = strdup(data->state.up.user); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; + char *decoded; + result = Curl_urldecode(NULL, data->state.up.user, 0, &decoded, NULL, + conn->handler->flags&PROTOPT_USERPWDCTRL ? + REJECT_ZERO : REJECT_CTRL); + if(result) + return result; + conn->user = decoded; conn->bits.user_passwd = TRUE; } else if(uc != CURLUE_NO_USER) return Curl_uc_to_curlcode(uc); - uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, - CURLU_URLDECODE); + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); if(!uc) { - conn->passwd = strdup(data->state.up.password); - if(!conn->passwd) - return CURLE_OUT_OF_MEMORY; + char *decoded; + result = Curl_urldecode(NULL, data->state.up.password, 0, &decoded, NULL, + conn->handler->flags&PROTOPT_USERPWDCTRL ? + REJECT_ZERO : REJECT_CTRL); + if(result) + return result; + conn->passwd = decoded; conn->bits.user_passwd = TRUE; } else if(uc != CURLUE_NO_PASSWORD) @@ -1991,55 +1953,27 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, } else { unsigned long port = strtoul(data->state.up.port, NULL, 10); - conn->remote_port = curlx_ultous(port); + conn->port = conn->remote_port = curlx_ultous(port); } (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); hostname = data->state.up.hostname; - if(!hostname) - /* this is for file:// transfers, get a dummy made */ - hostname = (char *)""; - - if(hostname[0] == '[') { + if(hostname && hostname[0] == '[') { /* This looks like an IPv6 address literal. See if there is an address scope. */ - char *zoneid; size_t hlen; - uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); conn->bits.ipv6_ip = TRUE; - /* cut off the brackets! */ hostname++; hlen = strlen(hostname); hostname[hlen - 1] = 0; - if(!uc && zoneid) { - char *endp; - unsigned long scope; - scope = strtoul(zoneid, &endp, 10); - if(!*endp && (scope < UINT_MAX)) { - /* A plain number, use it direcly as a scope id. */ - conn->scope_id = (unsigned int)scope; - } -#ifdef HAVE_IF_NAMETOINDEX - else { - /* Zone identifier is not numeric */ - unsigned int scopeidx = 0; - scopeidx = if_nametoindex(zoneid); - if(!scopeidx) - infof(data, "Invalid zoneid id: %s; %s\n", zoneid, - strerror(errno)); - else - conn->scope_id = scopeidx; - } -#endif /* HAVE_IF_NAMETOINDEX */ - free(zoneid); - } + zonefrom_url(uh, conn); } /* make sure the connect struct gets its own copy of the host name */ - conn->host.rawalloc = strdup(hostname); + conn->host.rawalloc = strdup(hostname ? hostname : ""); if(!conn->host.rawalloc) return CURLE_OUT_OF_MEMORY; conn->host.name = conn->host.rawalloc; @@ -2095,9 +2029,8 @@ static CURLcode setup_range(struct Curl_easy *data) */ static CURLcode setup_connection_internals(struct connectdata *conn) { - const struct Curl_handler * p; + const struct Curl_handler *p; CURLcode result; - conn->socktype = SOCK_STREAM; /* most of them are TCP streams */ /* Perform setup complement if some. */ p = conn->handler; @@ -2128,6 +2061,11 @@ void Curl_free_request_state(struct Curl_easy *data) { Curl_safefree(data->req.protop); Curl_safefree(data->req.newurl); + +#ifndef CURL_DISABLE_DOH + Curl_close(&data->req.doh.probe[0].easy); + Curl_close(&data->req.doh.probe[1].easy); +#endif } @@ -2298,7 +2236,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, struct connectdata *conn, char *proxy, curl_proxytype proxytype) { - char *portptr; + char *portptr = NULL; long port = -1; char *proxyuser = NULL; char *proxypasswd = NULL; @@ -2422,6 +2360,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, size_t len = strlen(host); host[len-1] = 0; /* clear the trailing bracket */ host++; + zonefrom_url(uhp, conn); } proxyinfo->host.name = host; @@ -2437,26 +2376,16 @@ static CURLcode parse_proxy(struct Curl_easy *data, static CURLcode parse_proxy_auth(struct Curl_easy *data, struct connectdata *conn) { - char proxyuser[MAX_CURL_USER_LENGTH]=""; - char proxypasswd[MAX_CURL_PASSWORD_LENGTH]=""; - CURLcode result; - - if(data->set.str[STRING_PROXYUSERNAME] != NULL) { - strncpy(proxyuser, data->set.str[STRING_PROXYUSERNAME], - MAX_CURL_USER_LENGTH); - proxyuser[MAX_CURL_USER_LENGTH-1] = '\0'; /*To be on safe side*/ - } - if(data->set.str[STRING_PROXYPASSWORD] != NULL) { - strncpy(proxypasswd, data->set.str[STRING_PROXYPASSWORD], - MAX_CURL_PASSWORD_LENGTH); - proxypasswd[MAX_CURL_PASSWORD_LENGTH-1] = '\0'; /*To be on safe side*/ - } + char *proxyuser = data->set.str[STRING_PROXYUSERNAME]; + char *proxypasswd = data->set.str[STRING_PROXYPASSWORD]; + CURLcode result = CURLE_OK; - result = Curl_urldecode(data, proxyuser, 0, &conn->http_proxy.user, NULL, - FALSE); - if(!result) + if(proxyuser) + result = Curl_urldecode(data, proxyuser, 0, &conn->http_proxy.user, NULL, + REJECT_ZERO); + if(!result && proxypasswd) result = Curl_urldecode(data, proxypasswd, 0, &conn->http_proxy.passwd, - NULL, FALSE); + NULL, REJECT_ZERO); return result; } @@ -2674,6 +2603,12 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, size_t plen; size_t olen; + /* the input length check is because this is called directcly from setopt + and isn't going through the regular string length check */ + size_t llen = strlen(login); + if(llen > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + /* Attempt to find the password separator */ if(passwdp) { psep = strchr(login, ':'); @@ -2846,8 +2781,7 @@ static CURLcode override_login(struct Curl_easy *data, &netrc_user_changed, &netrc_passwd_changed, data->set.str[STRING_NETRC_FILE]); if(ret > 0) { - infof(data, "Couldn't find host %s in the " - DOT_CHAR "netrc file; using defaults\n", + infof(data, "Couldn't find host %s in the .netrc file; using defaults\n", conn->host.name); } else if(ret < 0) { @@ -2871,12 +2805,14 @@ static CURLcode override_login(struct Curl_easy *data, /* for updated strings, we update them in the URL */ if(user_changed) { - uc = curl_url_set(data->state.uh, CURLUPART_USER, *userp, 0); + uc = curl_url_set(data->state.uh, CURLUPART_USER, *userp, + CURLU_URLENCODE); if(uc) return Curl_uc_to_curlcode(uc); } if(passwd_changed) { - uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, *passwdp, 0); + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, *passwdp, + CURLU_URLENCODE); if(uc) return Curl_uc_to_curlcode(uc); } @@ -2913,13 +2849,6 @@ static CURLcode set_login(struct connectdata *conn) result = CURLE_OUT_OF_MEMORY; } - /* if there's a user without password, consider password blank */ - if(conn->user && !conn->passwd) { - conn->passwd = strdup(""); - if(!conn->passwd) - result = CURLE_OUT_OF_MEMORY; - } - return result; } @@ -3142,28 +3071,74 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, #ifdef USE_ALTSVC if(data->asi && !host && (port == -1) && - (conn->handler->protocol == CURLPROTO_HTTPS)) { + ((conn->handler->protocol == CURLPROTO_HTTPS) || +#ifdef CURLDEBUG + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_ALTSVC_HTTP") +#else + 0 +#endif + )) { /* no connect_to match, try alt-svc! */ - const char *nhost; - int nport; - enum alpnid nalpnid; + enum alpnid srcalpnid; bool hit; + struct altsvc *as; + const int allowed_versions = ( ALPN_h1 +#ifdef USE_NGHTTP2 + | ALPN_h2 +#endif +#ifdef ENABLE_QUIC + | ALPN_h3 +#endif + ) & data->asi->flags; + host = conn->host.rawalloc; +#ifdef USE_NGHTTP2 + /* with h2 support, check that first */ + srcalpnid = ALPN_h2; hit = Curl_altsvc_lookup(data->asi, - ALPN_h1, host, conn->remote_port, /* from */ - &nalpnid, &nhost, &nport /* to */); + srcalpnid, host, conn->remote_port, /* from */ + &as /* to */, + allowed_versions); + if(!hit) +#endif + { + srcalpnid = ALPN_h1; + hit = Curl_altsvc_lookup(data->asi, + srcalpnid, host, conn->remote_port, /* from */ + &as /* to */, + allowed_versions); + } if(hit) { - char *hostd = strdup((char *)nhost); + char *hostd = strdup((char *)as->dst.host); if(!hostd) return CURLE_OUT_OF_MEMORY; conn->conn_to_host.rawalloc = hostd; conn->conn_to_host.name = hostd; conn->bits.conn_to_host = TRUE; - conn->conn_to_port = nport; + 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\n", - Curl_alpnid2str(ALPN_h1), host, conn->remote_port, - Curl_alpnid2str(nalpnid), hostd, nport); + Curl_alpnid2str(srcalpnid), host, conn->remote_port, + Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); + if(srcalpnid != as->dst.alpnid) { + /* protocol version switch */ + switch(as->dst.alpnid) { + case ALPN_h1: + conn->httpversion = 11; + break; + case ALPN_h2: + conn->httpversion = 20; + break; + case ALPN_h3: + conn->transport = TRNSPRT_QUIC; + conn->httpversion = 30; + break; + default: /* shouldn't be possible */ + break; + } + } } } #endif @@ -3210,7 +3185,7 @@ static CURLcode resolve_server(struct Curl_easy *data, else { bool longpath = FALSE; hostaddr->addr = Curl_unix2addr(path, &longpath, - conn->abstract_unix_socket); + conn->bits.abstract_unix_socket); if(hostaddr->addr) hostaddr->inuse++; else { @@ -3228,6 +3203,7 @@ static CURLcode resolve_server(struct Curl_easy *data, } else #endif + if(!conn->bits.proxy) { struct hostname *connhost; if(conn->bits.conn_to_host) @@ -3256,10 +3232,11 @@ static CURLcode resolve_server(struct Curl_easy *data, else if(!hostaddr) { failf(data, "Couldn't resolve host '%s'", connhost->dispname); - result = CURLE_COULDNT_RESOLVE_HOST; + result = CURLE_COULDNT_RESOLVE_HOST; /* don't return yet, we need to clean up the timeout first */ } } +#ifndef CURL_DISABLE_PROXY else { /* This is a proxy that hasn't been resolved yet. */ @@ -3285,6 +3262,7 @@ static CURLcode resolve_server(struct Curl_easy *data, /* don't return yet, we need to clean up the timeout first */ } } +#endif DEBUGASSERT(conn->dns_entry == NULL); conn->dns_entry = hostaddr; } @@ -3300,16 +3278,17 @@ static CURLcode resolve_server(struct Curl_easy *data, static void reuse_conn(struct connectdata *old_conn, struct connectdata *conn) { - free_idnconverted_hostname(&old_conn->http_proxy.host); - free_idnconverted_hostname(&old_conn->socks_proxy.host); +#ifndef CURL_DISABLE_PROXY + Curl_free_idnconverted_hostname(&old_conn->http_proxy.host); + Curl_free_idnconverted_hostname(&old_conn->socks_proxy.host); free(old_conn->http_proxy.host.rawalloc); free(old_conn->socks_proxy.host.rawalloc); - + Curl_free_primary_ssl_config(&old_conn->proxy_ssl_config); +#endif /* free the SSL config struct from this connection struct as this was allocated in vain and is targeted for destruction */ Curl_free_primary_ssl_config(&old_conn->ssl_config); - Curl_free_primary_ssl_config(&old_conn->proxy_ssl_config); conn->data = old_conn->data; @@ -3326,6 +3305,7 @@ static void reuse_conn(struct connectdata *old_conn, old_conn->passwd = NULL; } +#ifndef CURL_DISABLE_PROXY conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd; if(conn->bits.proxy_user_passwd) { /* use the new proxy user name and proxy password though */ @@ -3342,11 +3322,16 @@ static void reuse_conn(struct connectdata *old_conn, old_conn->http_proxy.passwd = NULL; old_conn->socks_proxy.passwd = NULL; } + Curl_safefree(old_conn->http_proxy.user); + Curl_safefree(old_conn->socks_proxy.user); + Curl_safefree(old_conn->http_proxy.passwd); + Curl_safefree(old_conn->socks_proxy.passwd); +#endif /* host can change, when doing keepalive with a proxy or if the case is different this time etc */ - free_idnconverted_hostname(&conn->host); - free_idnconverted_hostname(&conn->conn_to_host); + Curl_free_idnconverted_hostname(&conn->host); + Curl_free_idnconverted_hostname(&conn->conn_to_host); Curl_safefree(conn->host.rawalloc); Curl_safefree(conn->conn_to_host.rawalloc); conn->host = old_conn->host; @@ -3369,10 +3354,6 @@ static void reuse_conn(struct connectdata *old_conn, Curl_safefree(old_conn->user); Curl_safefree(old_conn->passwd); Curl_safefree(old_conn->options); - Curl_safefree(old_conn->http_proxy.user); - Curl_safefree(old_conn->socks_proxy.user); - Curl_safefree(old_conn->http_proxy.passwd); - Curl_safefree(old_conn->socks_proxy.passwd); Curl_safefree(old_conn->localdev); Curl_llist_destroy(&old_conn->easyq, NULL); @@ -3442,9 +3423,9 @@ static CURLcode create_conn(struct Curl_easy *data, if(result) goto out; - if(data->set.str[STRING_BEARER]) { - conn->oauth_bearer = strdup(data->set.str[STRING_BEARER]); - if(!conn->oauth_bearer) { + if(data->set.str[STRING_SASL_AUTHZID]) { + conn->sasl_authzid = strdup(data->set.str[STRING_SASL_AUTHZID]); + if(!conn->sasl_authzid) { result = CURLE_OUT_OF_MEMORY; goto out; } @@ -3457,7 +3438,7 @@ static CURLcode create_conn(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; goto out; } - conn->abstract_unix_socket = data->set.abstract_unix_socket; + conn->bits.abstract_unix_socket = data->set.abstract_unix_socket; } #endif @@ -3467,7 +3448,6 @@ static CURLcode create_conn(struct Curl_easy *data, result = create_conn_helper_init_proxy(conn); if(result) goto out; -#endif /************************************************************* * If the protocol is using SSL and HTTP proxy is used, we set @@ -3475,6 +3455,7 @@ static CURLcode create_conn(struct Curl_easy *data, *************************************************************/ if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy) conn->bits.tunnel_proxy = TRUE; +#endif /************************************************************* * Figure out the remote port number and fix it in the URL @@ -3505,24 +3486,26 @@ static CURLcode create_conn(struct Curl_easy *data, /************************************************************* * IDN-convert the hostnames *************************************************************/ - result = idnconvert_hostname(conn, &conn->host); + result = Curl_idnconvert_hostname(conn, &conn->host); if(result) goto out; if(conn->bits.conn_to_host) { - result = idnconvert_hostname(conn, &conn->conn_to_host); + result = Curl_idnconvert_hostname(conn, &conn->conn_to_host); if(result) goto out; } +#ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy) { - result = idnconvert_hostname(conn, &conn->http_proxy.host); + result = Curl_idnconvert_hostname(conn, &conn->http_proxy.host); if(result) goto out; } if(conn->bits.socksproxy) { - result = idnconvert_hostname(conn, &conn->socks_proxy.host); + result = Curl_idnconvert_hostname(conn, &conn->socks_proxy.host); if(result) goto out; } +#endif /************************************************************* * Check whether the host and the "connect to host" are equal. @@ -3541,6 +3524,7 @@ static CURLcode create_conn(struct Curl_easy *data, conn->bits.conn_to_port = FALSE; } +#ifndef CURL_DISABLE_PROXY /************************************************************* * If the "connect to" feature is used with an HTTP proxy, * we set the tunnel_proxy bit. @@ -3548,6 +3532,7 @@ static CURLcode create_conn(struct Curl_easy *data, if((conn->bits.conn_to_host || conn->bits.conn_to_port) && conn->bits.httpproxy) conn->bits.tunnel_proxy = TRUE; +#endif /************************************************************* * Setup internals depending on protocol. Needs to be done after @@ -3580,6 +3565,7 @@ static CURLcode create_conn(struct Curl_easy *data, if(!result) { conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */ + Curl_attach_connnection(data, conn); result = Curl_conncache_add_conn(data->state.conn_cache, conn); if(result) goto out; @@ -3594,7 +3580,6 @@ static CURLcode create_conn(struct Curl_easy *data, (void)conn->handler->done(conn, result, FALSE); goto out; } - Curl_attach_connnection(data, conn); Curl_setup_transfer(data, -1, -1, FALSE, -1); } @@ -3615,57 +3600,75 @@ static CURLcode create_conn(struct Curl_easy *data, copies will be separately allocated. */ data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_ORIG]; - data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_ORIG]; - data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; data->set.ssl.primary.random_file = data->set.str[STRING_SSL_RANDOM_FILE]; - data->set.proxy_ssl.primary.random_file = - data->set.str[STRING_SSL_RANDOM_FILE]; data->set.ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET]; - data->set.proxy_ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET]; data->set.ssl.primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST_ORIG]; - data->set.proxy_ssl.primary.cipher_list = - data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; data->set.ssl.primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_ORIG]; + data->set.ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; + data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_ORIG]; + +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; + data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; + data->set.proxy_ssl.primary.random_file = + data->set.str[STRING_SSL_RANDOM_FILE]; + data->set.proxy_ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET]; + data->set.proxy_ssl.primary.cipher_list = + data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; data->set.proxy_ssl.primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; - - data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_ORIG]; + data->set.proxy_ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; + data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; data->set.proxy_ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; - data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_ORIG]; data->set.proxy_ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; - data->set.ssl.cert = data->set.str[STRING_CERT_ORIG]; data->set.proxy_ssl.cert = data->set.str[STRING_CERT_PROXY]; - data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE_ORIG]; data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; - data->set.ssl.key = data->set.str[STRING_KEY_ORIG]; data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY]; - data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE_ORIG]; data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; - data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_ORIG]; data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; - data->set.ssl.primary.clientcert = data->set.str[STRING_CERT_ORIG]; data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY]; + data->set.proxy_ssl.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; + data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY]; +#endif + data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_ORIG]; + data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_ORIG]; + data->set.ssl.cert = data->set.str[STRING_CERT_ORIG]; + data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE_ORIG]; + data->set.ssl.key = data->set.str[STRING_KEY_ORIG]; + data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE_ORIG]; + data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_ORIG]; + data->set.ssl.primary.clientcert = data->set.str[STRING_CERT_ORIG]; #ifdef USE_TLS_SRP data->set.ssl.username = data->set.str[STRING_TLSAUTH_USERNAME_ORIG]; - data->set.proxy_ssl.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; data->set.ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD_ORIG]; +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; data->set.proxy_ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; #endif +#endif + + data->set.ssl.cert_blob = data->set.blobs[BLOB_CERT_ORIG]; + data->set.ssl.key_blob = data->set.blobs[BLOB_KEY_ORIG]; + data->set.ssl.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_ORIG]; if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary, - &conn->ssl_config)) { + &conn->ssl_config)) { result = CURLE_OUT_OF_MEMORY; goto out; } +#ifndef CURL_DISABLE_PROXY if(!Curl_clone_primary_ssl_config(&data->set.proxy_ssl.primary, &conn->proxy_ssl_config)) { result = CURLE_OUT_OF_MEMORY; goto out; } +#endif prune_dead_connections(data); @@ -3688,25 +3691,6 @@ static CURLcode create_conn(struct Curl_easy *data, else reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe); - /* If we found a reusable connection that is now marked as in use, we may - still want to open a new connection if we are multiplexing. */ - if(reuse && !force_reuse && IsMultiplexingPossible(data, conn_temp)) { - size_t multiplexed = CONN_INUSE(conn_temp); - if(multiplexed > 0) { - infof(data, "Found connection %ld, with %zu requests on it\n", - conn_temp->connection_id, multiplexed); - - if(Curl_conncache_bundle_size(conn_temp) < max_host_connections && - Curl_conncache_size(data) < max_total_connections) { - /* We want a new connection anyway */ - reuse = FALSE; - - infof(data, "We can reuse, but we want a new connection anyway\n"); - Curl_conncache_return_conn(conn_temp); - } - } - } - if(reuse) { /* * We already have a connection for this, we got the former connection @@ -3722,12 +3706,17 @@ static CURLcode create_conn(struct Curl_easy *data, conn = conn_temp; *in_connect = conn; +#ifndef CURL_DISABLE_PROXY infof(data, "Re-using existing connection! (#%ld) with %s %s\n", conn->connection_id, conn->bits.proxy?"proxy":"host", conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : conn->http_proxy.host.name ? conn->http_proxy.host.dispname : - conn->host.dispname); + conn->host.dispname); +#else + infof(data, "Re-using existing connection! (#%ld) with host %s\n", + conn->connection_id, conn->host.dispname); +#endif } else { /* We have decided that we want a new connection. However, we may not @@ -3749,8 +3738,9 @@ static CURLcode create_conn(struct Curl_easy *data, connections_available = FALSE; else { /* this gets a lock on the conncache */ + const char *bundlehost; struct connectbundle *bundle = - Curl_conncache_find_bundle(conn, data->state.conn_cache); + Curl_conncache_find_bundle(conn, data->state.conn_cache, &bundlehost); if(max_host_connections > 0 && bundle && (bundle->num_connections >= max_host_connections)) { @@ -3758,19 +3748,19 @@ static CURLcode create_conn(struct Curl_easy *data, /* The bundle is full. Extract the oldest connection. */ conn_candidate = Curl_conncache_extract_bundle(data, bundle); - Curl_conncache_unlock(data); + CONNCACHE_UNLOCK(data); if(conn_candidate) (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE); else { - infof(data, "No more connections allowed to host: %zu\n", - max_host_connections); + infof(data, "No more connections allowed to host %s: %zu\n", + bundlehost, max_host_connections); connections_available = FALSE; } } else - Curl_conncache_unlock(data); + CONNCACHE_UNLOCK(data); } @@ -3804,6 +3794,8 @@ static CURLcode create_conn(struct Curl_easy *data, * This is a brand new connection, so let's store it in the connection * cache of ours! */ + Curl_attach_connnection(data, conn); + result = Curl_conncache_add_conn(data->state.conn_cache, conn); if(result) goto out; @@ -3855,10 +3847,12 @@ static CURLcode create_conn(struct Curl_easy *data, /* Strip trailing dots. resolve_server copied the name. */ strip_trailing_dot(&conn->host); +#ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy) strip_trailing_dot(&conn->http_proxy.host); if(conn->bits.socksproxy) strip_trailing_dot(&conn->socks_proxy.host); +#endif if(conn->bits.conn_to_host) strip_trailing_dot(&conn->conn_to_host); @@ -3889,22 +3883,23 @@ CURLcode Curl_setup_conn(struct connectdata *conn, } *protocol_done = FALSE; /* default to not done */ +#ifndef CURL_DISABLE_PROXY /* set proxy_connect_closed to false unconditionally already here since it is used strictly to provide extra information to a parent function in the case of proxy CONNECT failures and we must make sure we don't have it lingering set from a previous invoke */ conn->bits.proxy_connect_closed = FALSE; - +#endif /* * Set user-agent. Used for HTTP, but since we can attempt to tunnel * basically anything through a http proxy we can't limit this based on * protocol. */ if(data->set.str[STRING_USERAGENT]) { - Curl_safefree(conn->allocptr.uagent); - conn->allocptr.uagent = + Curl_safefree(data->state.aptr.uagent); + data->state.aptr.uagent = aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]); - if(!conn->allocptr.uagent) + if(!data->state.aptr.uagent) return CURLE_OUT_OF_MEMORY; } @@ -3926,7 +3921,9 @@ CURLcode Curl_setup_conn(struct connectdata *conn, } else { Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ - Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ + if(conn->ssl[FIRSTSOCKET].use || + (conn->handler->protocol & PROTO_FAMILY_SSH)) + Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; *protocol_done = TRUE; Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]); @@ -3956,7 +3953,7 @@ CURLcode Curl_connect(struct Curl_easy *data, result = create_conn(data, &conn, asyncp); if(!result) { - if(CONN_INUSE(conn)) + if(CONN_INUSE(conn) > 1) /* multiplexed */ *protocol_done = TRUE; else if(!*asyncp) { @@ -3973,11 +3970,10 @@ CURLcode Curl_connect(struct Curl_easy *data, else if(result && conn) { /* We're not allowed to return failure with memory left allocated in the connectdata struct, free those here */ + Curl_detach_connnection(data); + Curl_conncache_remove_conn(data, conn, TRUE); Curl_disconnect(data, conn, TRUE); } - else if(!result && !data->conn) - /* FILE: transfers already have the connection attached */ - Curl_attach_connnection(data, conn); return result; } @@ -3996,6 +3992,11 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) { struct SingleRequest *k = &data->req; + /* if this is a pushed stream, we need this: */ + CURLcode result = Curl_preconnect(data); + if(result) + return result; + if(conn) { conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */ @@ -4008,30 +4009,17 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) data->state.done = FALSE; /* *_done() is not called yet */ data->state.expect100header = FALSE; - if(data->set.opt_no_body) /* in HTTP lingo, no body means using the HEAD request... */ - data->set.httpreq = HTTPREQ_HEAD; - else if(HTTPREQ_HEAD == data->set.httpreq) - /* ... but if unset there really is no perfect method that is the - "opposite" of HEAD but in reality most people probably think GET - then. The important thing is that we can't let it remain HEAD if the - opt_no_body is set FALSE since then we'll behave wrong when getting - HTTP. */ - data->set.httpreq = HTTPREQ_GET; + data->state.httpreq = HTTPREQ_HEAD; k->start = Curl_now(); /* start time */ k->now = k->start; /* current time is now */ k->header = TRUE; /* assume header */ - k->bytecount = 0; - - k->buf = data->state.buffer; - k->hbufp = data->state.headerbuff; k->ignorebody = FALSE; Curl_speedinit(data); - Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); @@ -4147,34 +4135,3 @@ static unsigned int get_protocol_family(unsigned int protocol) return family; } - - -/* - * Wrapper to call functions in Curl_conncache_foreach() - * - * Returns always 0. - */ -static int conn_upkeep(struct connectdata *conn, - void *param) -{ - /* Param is unused. */ - (void)param; - - if(conn->handler->connection_check) { - /* Do a protocol-specific keepalive check on the connection. */ - conn->handler->connection_check(conn, CONNCHECK_KEEPALIVE); - } - - return 0; /* continue iteration */ -} - -CURLcode Curl_upkeep(struct conncache *conn_cache, - void *data) -{ - /* Loop over every connection and make connection alive. */ - Curl_conncache_foreach(data, - conn_cache, - data, - conn_upkeep); - return CURLE_OK; -} diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h index 4db9e86532c..1941dc6a4f5 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -47,47 +47,40 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_open(struct Curl_easy **curl); CURLcode Curl_init_userdefined(struct Curl_easy *data); -void Curl_freeset(struct Curl_easy * data); -/* free the URL pieces */ -void Curl_up_free(struct Curl_easy *data); +void Curl_freeset(struct Curl_easy *data); CURLcode Curl_uc_to_curlcode(CURLUcode uc); -CURLcode Curl_close(struct Curl_easy *data); /* opposite of curl_open() */ +CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */ CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect); CURLcode Curl_disconnect(struct Curl_easy *data, struct connectdata *, bool dead_connection); -CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done); -CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done); -CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done); CURLcode Curl_setup_conn(struct connectdata *conn, bool *protocol_done); void Curl_free_request_state(struct Curl_easy *data); - -int Curl_protocol_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks); -int Curl_doing_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks); CURLcode Curl_parse_login_details(const char *login, const size_t len, char **userptr, char **passwdptr, char **optionsptr); -void Curl_close_connections(struct Curl_easy *data); -CURLcode Curl_upkeep(struct conncache *conn_cache, void *data); const struct Curl_handler *Curl_builtin_scheme(const char *scheme); +bool Curl_is_ASCII_name(const char *hostname); +CURLcode Curl_idnconvert_hostname(struct connectdata *conn, + struct hostname *host); +void Curl_free_idnconverted_hostname(struct hostname *host); + #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless specified */ -CURLcode Curl_connected_proxy(struct connectdata *conn, int sockindex); - #ifdef CURL_DISABLE_VERBOSE_STRINGS #define Curl_verboseconnect(x) Curl_nop_stmt #else void Curl_verboseconnect(struct connectdata *conn); #endif +#ifdef CURL_DISABLE_PROXY +#define CONNECT_PROXY_SSL() FALSE +#else + #define CONNECT_PROXY_SSL()\ (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ !conn->bits.proxy_ssl_connected[sockindex]) @@ -99,5 +92,6 @@ void Curl_verboseconnect(struct connectdata *conn); #define CONNECT_SECONDARYSOCKET_PROXY_SSL()\ (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ !conn->bits.proxy_ssl_connected[SECONDARYSOCKET]) +#endif /* !CURL_DISABLE_PROXY */ #endif /* HEADER_CURL_URL_H */ diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h index 5f059c203a3..d14d53d97a9 100644 --- a/Utilities/cmcurl/lib/urlapi-int.h +++ b/Utilities/cmcurl/lib/urlapi-int.h @@ -26,9 +26,6 @@ #define MAX_SCHEME_LEN 40 bool Curl_is_absolute_url(const char *url, char *scheme, size_t buflen); -char *Curl_concat_url(const char *base, const char *relurl); -size_t Curl_strlen_url(const char *url, bool relative); -void Curl_strcpy_url(char *output, const char *url, bool relative); #ifdef DEBUGBUILD CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname); diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index d07e4f5dffc..acbfb82875d 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,6 +29,7 @@ #include "url.h" #include "escape.h" #include "curl_ctype.h" +#include "inet_pton.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -63,6 +64,7 @@ struct Curl_URL { char *fragment; char *scratch; /* temporary scratch area */ + char *temppath; /* temporary path pointer */ long portnum; /* the numerical version */ }; @@ -81,6 +83,7 @@ static void free_urlhandle(struct Curl_URL *u) free(u->query); free(u->fragment); free(u->scratch); + free(u->temppath); } /* move the full contents of one handle onto another and @@ -222,7 +225,7 @@ static void strcpy_url(char *output, const char *url, bool relative) break; } } - *optr = 0; /* zero terminate output buffer */ + *optr = 0; /* null-terminate output buffer */ } @@ -350,7 +353,7 @@ static char *concat_url(const char *base, const char *relurl) else { /* We got a new absolute path for this server */ - if((relurl[0] == '/') && (relurl[1] == '/')) { + if(relurl[1] == '/') { /* the new URL starts with //, just keep the protocol part from the original one */ *protsep = 0; @@ -425,7 +428,6 @@ static char *concat_url(const char *base, const char *relurl) * */ static CURLUcode parse_hostname_login(struct Curl_URL *u, - const struct Curl_handler *h, char **hostname, unsigned int flags) { @@ -434,6 +436,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, char *userp = NULL; char *passwdp = NULL; char *optionsp = NULL; + const struct Curl_handler *h = NULL; /* At this point, we're hoping all the other special cases have * been taken care of, so conn->host.name is at most @@ -453,6 +456,10 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, * ftp://user:password@ftp.my.site:8021/README */ *hostname = ++ptr; + /* if this is a known scheme, get some details */ + if(u->scheme) + h = Curl_builtin_scheme(u->scheme); + /* We could use the login information in the URL so extract it. Only parse options if the handler says we should. Note that 'h' might be NULL! */ ccode = Curl_parse_login_details(login, ptr - login - 1, @@ -568,7 +575,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname) } /* scan for byte values < 31 or 127 */ -static CURLUcode junkscan(char *part) +static CURLUcode junkscan(const char *part) { if(part) { static const char badbytes[]={ @@ -577,7 +584,7 @@ static CURLUcode junkscan(char *part) 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x7f, - 0x00 /* zero terminate */ + 0x00 /* null-terminate */ }; size_t n = strlen(part); size_t nfine = strcspn(part, badbytes); @@ -591,20 +598,26 @@ static CURLUcode junkscan(char *part) static CURLUcode hostname_check(struct Curl_URL *u, char *hostname) { - const char *l = NULL; /* accepted characters */ size_t len; size_t hlen = strlen(hostname); if(hostname[0] == '[') { +#ifdef ENABLE_IPV6 + char dest[16]; /* fits a binary IPv6 address */ +#endif + const char *l = "0123456789abcdefABCDEF:."; + if(hlen < 4) /* '[::]' is the shortest possible valid string */ + return CURLUE_MALFORMED_INPUT; hostname++; - l = "0123456789abcdefABCDEF::."; hlen -= 2; - } - if(l) { + if(hostname[hlen] != ']') + return CURLUE_MALFORMED_INPUT; + /* only valid letters are ok */ len = strspn(hostname, l); if(hlen != len) { + hlen = len; if(hostname[len] == '%') { /* this could now be '%[zone id]' */ char zoneid[16]; @@ -628,6 +641,12 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname) return CURLUE_MALFORMED_INPUT; /* hostname is fine */ } +#ifdef ENABLE_IPV6 + hostname[hlen] = 0; /* end the address there */ + if(1 != Curl_inet_pton(AF_INET6, hostname, dest)) + return CURLUE_MALFORMED_INPUT; + hostname[hlen] = ']'; /* restore ending bracket */ +#endif } else { /* letters from the second string is not ok */ @@ -653,10 +672,9 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) CURLUcode result; bool url_has_scheme = FALSE; char schemebuf[MAX_SCHEME_LEN + 1]; - char *schemep = NULL; + const char *schemep = NULL; size_t schemelen = 0; size_t urllen; - const struct Curl_handler *h = NULL; if(!url) return CURLUE_MALFORMED_INPUT; @@ -775,6 +793,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(junkscan(schemep)) return CURLUE_MALFORMED_INPUT; + } else { /* no scheme! */ @@ -782,7 +801,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME))) return CURLUE_MALFORMED_INPUT; if(flags & CURLU_DEFAULT_SCHEME) - schemep = (char *) DEFAULT_SCHEME; + schemep = DEFAULT_SCHEME; /* * The URL was badly formatted, let's try without scheme specified. @@ -795,74 +814,83 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) p++; len = p - hostp; - if(!len) - return CURLUE_MALFORMED_INPUT; - - memcpy(hostname, hostp, len); - hostname[len] = 0; - - if((flags & CURLU_GUESS_SCHEME) && !schemep) { - /* legacy curl-style guess based on host name */ - if(checkprefix("ftp.", hostname)) - schemep = (char *)"ftp"; - else if(checkprefix("dict.", hostname)) - schemep = (char *)"dict"; - else if(checkprefix("ldap.", hostname)) - schemep = (char *)"ldap"; - else if(checkprefix("imap.", hostname)) - schemep = (char *)"imap"; - else if(checkprefix("smtp.", hostname)) - schemep = (char *)"smtp"; - else if(checkprefix("pop3.", hostname)) - schemep = (char *)"pop3"; - else - schemep = (char *)"http"; + if(len) { + memcpy(hostname, hostp, len); + hostname[len] = 0; + } + else { + if(!(flags & CURLU_NO_AUTHORITY)) + return CURLUE_MALFORMED_INPUT; } len = strlen(p); memcpy(path, p, len); path[len] = 0; - u->scheme = strdup(schemep); - if(!u->scheme) - return CURLUE_OUT_OF_MEMORY; + if(schemep) { + u->scheme = strdup(schemep); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + } } - /* if this is a known scheme, get some details */ - h = Curl_builtin_scheme(u->scheme); - if(junkscan(path)) return CURLUE_MALFORMED_INPUT; - query = strchr(path, '?'); - if(query) - *query++ = 0; + if((flags & CURLU_URLENCODE) && path[0]) { + /* worst case output length is 3x the original! */ + char *newp = malloc(strlen(path) * 3); + if(!newp) + return CURLUE_OUT_OF_MEMORY; + path_alloced = TRUE; + strcpy_url(newp, path, TRUE); /* consider it relative */ + u->temppath = path = newp; + } - fragment = strchr(query?query:path, '#'); - if(fragment) + fragment = strchr(path, '#'); + if(fragment) { *fragment++ = 0; + if(fragment[0]) { + u->fragment = strdup(fragment); + if(!u->fragment) + return CURLUE_OUT_OF_MEMORY; + } + } + + query = strchr(path, '?'); + if(query) { + *query++ = 0; + /* done even if the query part is a blank string */ + u->query = strdup(query); + if(!u->query) + return CURLUE_OUT_OF_MEMORY; + } if(!path[0]) - /* if there's no path set, unset */ + /* if there's no path left set, unset */ path = NULL; - else if(!(flags & CURLU_PATH_AS_IS)) { - /* sanitise paths and remove ../ and ./ sequences according to RFC3986 */ - char *newp = Curl_dedotdotify(path); - if(!newp) - return CURLUE_OUT_OF_MEMORY; + else { + if(!(flags & CURLU_PATH_AS_IS)) { + /* remove ../ and ./ sequences according to RFC3986 */ + char *newp = Curl_dedotdotify(path); + if(!newp) + return CURLUE_OUT_OF_MEMORY; - if(strcmp(newp, path)) { - /* if we got a new version */ - path = newp; - path_alloced = TRUE; + if(strcmp(newp, path)) { + /* if we got a new version */ + if(path_alloced) + Curl_safefree(u->temppath); + u->temppath = path = newp; + path_alloced = TRUE; + } + else + free(newp); } - else - free(newp); - } - if(path) { + u->path = path_alloced?path:strdup(path); if(!u->path) return CURLUE_OUT_OF_MEMORY; + u->temppath = NULL; /* used now */ } if(hostname) { @@ -872,7 +900,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(junkscan(hostname)) return CURLUE_MALFORMED_INPUT; - result = parse_hostname_login(u, h, &hostname, flags); + result = parse_hostname_login(u, &hostname, flags); if(result) return result; @@ -880,28 +908,44 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(result) return result; - result = hostname_check(u, hostname); - if(result) - return result; + if(0 == strlen(hostname) && (flags & CURLU_NO_AUTHORITY)) { + /* Skip hostname check, it's allowed to be empty. */ + } + else { + result = hostname_check(u, hostname); + if(result) + return result; + } u->host = strdup(hostname); if(!u->host) return CURLUE_OUT_OF_MEMORY; - } - if(query) { - u->query = strdup(query); - if(!u->query) - return CURLUE_OUT_OF_MEMORY; - } - if(fragment && fragment[0]) { - u->fragment = strdup(fragment); - if(!u->fragment) - return CURLUE_OUT_OF_MEMORY; + if((flags & CURLU_GUESS_SCHEME) && !schemep) { + /* legacy curl-style guess based on host name */ + if(checkprefix("ftp.", hostname)) + schemep = "ftp"; + else if(checkprefix("dict.", hostname)) + schemep = "dict"; + else if(checkprefix("ldap.", hostname)) + schemep = "ldap"; + else if(checkprefix("imap.", hostname)) + schemep = "imap"; + else if(checkprefix("smtp.", hostname)) + schemep = "smtp"; + else if(checkprefix("pop3.", hostname)) + schemep = "pop3"; + else + schemep = "http"; + + u->scheme = strdup(schemep); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + } } - free(u->scratch); - u->scratch = NULL; + Curl_safefree(u->scratch); + Curl_safefree(u->temppath); return CURLUE_OK; } @@ -1066,24 +1110,23 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, else return CURLUE_NO_SCHEME; - if(scheme) { - h = Curl_builtin_scheme(scheme); - if(!port && (flags & CURLU_DEFAULT_PORT)) { - /* there's no stored port number, but asked to deliver - a default one for the scheme */ - if(h) { - msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport); - port = portbuf; - } - } - else if(port) { - /* there is a stored port number, but asked to inhibit if it matches - the default one for the scheme */ - if(h && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) - port = NULL; + h = Curl_builtin_scheme(scheme); + if(!port && (flags & CURLU_DEFAULT_PORT)) { + /* there's no stored port number, but asked to deliver + a default one for the scheme */ + if(h) { + msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport); + port = portbuf; } } + else if(port) { + /* there is a stored port number, but asked to inhibit if it matches + the default one for the scheme */ + if(h && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) + port = NULL; + } + if(h && !(h->flags & PROTOPT_URLOPTIONS)) options = NULL; @@ -1142,7 +1185,10 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, if(urldecode) { char *decoded; size_t dlen; - CURLcode res = Curl_urldecode(NULL, *part, 0, &decoded, &dlen, TRUE); + /* this unconditional rejection of control bytes is documented + API behavior */ + CURLcode res = Curl_urldecode(NULL, *part, 0, &decoded, &dlen, + REJECT_CTRL); free(*part); if(res) { *part = NULL; @@ -1331,7 +1377,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, default: return CURLUE_UNKNOWN_PART; } - if(storep) { + DEBUGASSERT(storep); + { const char *newp = part; size_t nalloc = strlen(part); @@ -1351,7 +1398,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, i = (const unsigned char *)part; for(o = enc; *i; ++o, ++i) *o = (*i == ' ') ? '+' : *i; - *o = 0; /* zero terminate */ + *o = 0; /* null-terminate */ part = strdup(enc); if(!part) { free(enc); @@ -1375,7 +1422,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, o += 3; } } - *o = 0; /* zero terminate */ + *o = 0; /* null-terminate */ newp = enc; if(free_part) free((char *)part); @@ -1423,9 +1470,14 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } if(what == CURLUPART_HOST) { - if(hostname_check(u, (char *)newp)) { - free((char *)newp); - return CURLUE_MALFORMED_INPUT; + if(0 == strlen(newp) && (flags & CURLU_NO_AUTHORITY)) { + /* Skip hostname check, it's allowed to be empty. */ + } + else { + if(hostname_check(u, (char *)newp)) { + free((char *)newp); + return CURLUE_MALFORMED_INPUT; + } } } diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index d759592d9d1..f80a02defc4 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -49,6 +49,7 @@ #define PORT_RTMPT PORT_HTTP #define PORT_RTMPS PORT_HTTPS #define PORT_GOPHER 70 +#define PORT_MQTT 1883 #define DICT_MATCH "/MATCH:" #define DICT_MATCH2 "/M:" @@ -68,6 +69,7 @@ #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) #define DEFAULT_CONNCACHE_SIZE 5 @@ -79,7 +81,7 @@ */ #define RESP_TIMEOUT (120*1000) -/* Max string intput length is a precaution against abuse and to detect junk +/* Max string input length is a precaution against abuse and to detect junk input easier and better. */ #define CURL_MAX_INPUT_LENGTH 8000000 @@ -102,6 +104,7 @@ #include "hostip.h" #include "hash.h" #include "splay.h" +#include "dynbuf.h" /* return the count of bytes sent, or -1 on error */ typedef ssize_t (Curl_send)(struct connectdata *conn, /* connection data */ @@ -123,12 +126,14 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */ #include "smtp.h" #include "ftp.h" #include "file.h" -#include "ssh.h" +#include "vssh/ssh.h" #include "http.h" #include "rtsp.h" #include "smb.h" +#include "mqtt.h" #include "wildcard.h" #include "multihandle.h" +#include "quic.h" #ifdef HAVE_GSSAPI # ifdef HAVE_GSSGNU @@ -148,16 +153,18 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */ #include #endif /* HAVE_LIBSSH2_H */ -/* Initial size of the buffer to store headers in, it'll be enlarged in case - of need. */ -#define HEADERSIZE 256 - #define CURLEASY_MAGIC_NUMBER 0xc0dedbadU #define GOOD_EASY_HANDLE(x) \ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) /* the type we use for storing a single boolean bit */ +#ifdef _MSC_VER +typedef bool bit; +#define BIT(x) bool x +#else typedef unsigned int bit; +#define BIT(x) bit x:1 +#endif #ifdef HAVE_GSSAPI /* Types needed for krb5-ftp connections */ @@ -165,7 +172,7 @@ struct krb5buffer { void *data; size_t size; size_t index; - bit eof_flag:1; + BIT(eof_flag); }; enum protection_level { @@ -208,7 +215,7 @@ struct ssl_connect_data { #if defined(USE_SSL) struct ssl_backend_data *backend; #endif - bit use:1; + BIT(use); }; struct ssl_primary_config { @@ -221,10 +228,12 @@ struct ssl_primary_config { char *egdsocket; /* path to file containing the EGD daemon socket */ char *cipher_list; /* list of ciphers to use */ char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ - bit verifypeer:1; /* set TRUE if this is desired */ - bit verifyhost:1; /* set TRUE if CN/SAN must match hostname */ - bit verifystatus:1; /* set TRUE if certificate status must be checked */ - bit sessionid:1; /* cache session IDs or not */ + char *pinned_key; + struct curl_blob *cert_blob; + BIT(verifypeer); /* set TRUE if this is desired */ + BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ + BIT(verifystatus); /* set TRUE if certificate status must be checked */ + BIT(sessionid); /* cache session IDs or not */ }; struct ssl_config_data { @@ -232,11 +241,14 @@ struct ssl_config_data { long certverifyresult; /* result from the certificate verification */ char *CRLfile; /* CRL to check certificate revocation */ char *issuercert;/* optional issuer certificate filename */ + struct curl_blob *issuercert_blob; curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ void *fsslctxp; /* parameter for call back */ char *cert; /* client certificate file name */ + struct curl_blob *cert_blob; char *cert_type; /* format for certificate (default: PEM)*/ char *key; /* private key file name */ + struct curl_blob *key_blob; char *key_type; /* format for private key (default: PEM) */ char *key_passwd; /* plain text private key password */ #ifdef USE_TLS_SRP @@ -244,10 +256,14 @@ struct ssl_config_data { char *password; /* TLS password (for, e.g., SRP) */ enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */ #endif - bit certinfo:1; /* gather lots of certificate info */ - bit falsestart:1; - bit enable_beast:1; /* allow this flaw for interoperability's sake*/ - bit no_revoke:1; /* disable SSL certificate revocation checks */ + BIT(certinfo); /* gather lots of certificate info */ + BIT(falsestart); + BIT(enable_beast); /* allow this flaw for interoperability's sake*/ + BIT(no_revoke); /* disable SSL certificate revocation checks */ + BIT(no_partialchain); /* don't accept partial certificate chains */ + BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation + list errors */ + BIT(native_ca_store); /* use the native ca store of operating system */ }; struct ssl_general_config { @@ -290,8 +306,8 @@ struct digestdata { char *qop; char *algorithm; int nc; /* nounce count */ - bit stale:1; /* set true for re-negotiation */ - bit userhash:1; + BIT(stale); /* set true for re-negotiation */ + BIT(userhash); #endif }; @@ -357,6 +373,14 @@ struct ntlmdata { unsigned char nonce[8]; void *target_info; /* TargetInfo received in the ntlm type-2 message */ unsigned int target_info_len; + +#if defined(NTLM_WB_ENABLED) + /* used for communication with Samba's winbind daemon helper ntlm_auth */ + curl_socket_t ntlm_auth_hlpr_socket; + pid_t ntlm_auth_hlpr_pid; + char *challenge; /* The received base64 encoded ntlm type-2 message */ + char *response; /* The generated base64 ntlm type-1/type-3 message */ +#endif #endif }; #endif @@ -385,10 +409,10 @@ struct negotiatedata { size_t output_token_length; #endif #endif - bool noauthpersist; - bool havenoauthpersist; - bool havenegdata; - bool havemultiplerequests; + BIT(noauthpersist); + BIT(havenoauthpersist); + BIT(havenegdata); + BIT(havemultiplerequests); }; #endif @@ -397,68 +421,75 @@ struct negotiatedata { * Boolean values that concerns this connection. */ struct ConnectBits { - /* always modify bits.close with the connclose() and connkeep() macros! */ - bool proxy_ssl_connected[2]; /* TRUE when SSL initialization for HTTPS proxy - is complete */ bool tcpconnect[2]; /* the TCP layer (or similar) is connected, this is set the first time on the first connect function call */ - bit close:1; /* if set, we close the connection after this request */ - bit reuse:1; /* if set, this is a re-used connection */ - bit conn_to_host:1; /* if set, this connection has a "connect to host" - that overrides the host in the URL */ - bit conn_to_port:1; /* if set, this connection has a "connect to port" - that overrides the port in the URL (remote port) */ - bit proxy:1; /* if set, this transfer is done through a proxy - any type */ - bit httpproxy:1; /* if set, this transfer is done through a http proxy */ - bit socksproxy:1; /* if set, this transfer is done through a socks proxy */ - bit user_passwd:1; /* do we use user+password for this connection? */ - bit proxy_user_passwd:1; /* user+password for the proxy? */ - bit ipv6_ip:1; /* we communicate with a remote site specified with pure IPv6 - IP address */ - bit ipv6:1; /* we communicate with a site using an IPv6 address */ - bit do_more:1; /* this is set TRUE if the ->curl_do_more() function is - supposed to be called, after ->curl_do() */ - bit protoconnstart:1;/* the protocol layer has STARTED its operation after - the TCP layer connect */ - bit retry:1; /* this connection is about to get closed and then - re-attempted at another connection. */ - bit tunnel_proxy:1; /* if CONNECT is used to "tunnel" through the proxy. - This is implicit when SSL-protocols are used through - proxies, but can also be enabled explicitly by - apps */ - bit authneg:1; /* TRUE when the auth phase has started, which means - that we are creating a request with an auth header, - but it is not the final request in the auth - negotiation. */ - bit rewindaftersend:1;/* TRUE when the sending couldn't be stopped even - though it will be discarded. When the whole send - operation is done, we must call the data rewind - callback. */ +#ifndef CURL_DISABLE_PROXY + bool proxy_ssl_connected[2]; /* TRUE when SSL initialization for HTTPS proxy + is complete */ + BIT(httpproxy); /* if set, this transfer is done through a http proxy */ + BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ + BIT(proxy_user_passwd); /* user+password for the proxy? */ + BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy. + This is implicit when SSL-protocols are used through + proxies, but can also be enabled explicitly by + apps */ + BIT(proxy_connect_closed); /* TRUE if a proxy disconnected the connection + in a CONNECT request with auth, so that + libcurl should reconnect and continue. */ +#endif + /* always modify bits.close with the connclose() and connkeep() macros! */ + BIT(close); /* if set, we close the connection after this request */ + BIT(reuse); /* if set, this is a re-used connection */ + BIT(altused); /* this is an alt-svc "redirect" */ + BIT(conn_to_host); /* if set, this connection has a "connect to host" + that overrides the host in the URL */ + BIT(conn_to_port); /* if set, this connection has a "connect to port" + that overrides the port in the URL (remote port) */ + BIT(proxy); /* if set, this transfer is done through a proxy - any type */ + BIT(user_passwd); /* do we use user+password for this connection? */ + 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() */ + BIT(protoconnstart);/* the protocol layer has STARTED its operation after + the TCP layer connect */ + BIT(retry); /* this connection is about to get closed and then + re-attempted at another connection. */ + BIT(authneg); /* TRUE when the auth phase has started, which means + that we are creating a request with an auth header, + but it is not the final request in the auth + negotiation. */ + BIT(rewindaftersend);/* TRUE when the sending couldn't be stopped even + though it will be discarded. When the whole send + operation is done, we must call the data rewind + callback. */ #ifndef CURL_DISABLE_FTP - bit ftp_use_epsv:1; /* As set with CURLOPT_FTP_USE_EPSV, but if we find out - EPSV doesn't work we disable it for the forthcoming - requests */ - bit ftp_use_eprt:1; /* As set with CURLOPT_FTP_USE_EPRT, but if we find out - EPRT doesn't work we disable it for the forthcoming - requests */ - bit ftp_use_data_ssl:1; /* Enabled SSL for the data connection */ + BIT(ftp_use_epsv); /* As set with CURLOPT_FTP_USE_EPSV, but if we find out + EPSV doesn't work we disable it for the forthcoming + requests */ + BIT(ftp_use_eprt); /* As set with CURLOPT_FTP_USE_EPRT, but if we find out + EPRT doesn't work we disable it for the forthcoming + requests */ + BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */ #endif - bit netrc:1; /* name+password provided by netrc */ - bit userpwd_in_url:1; /* name+password found in url */ - bit stream_was_rewound:1; /* The stream was rewound after a request read - past the end of its response byte boundary */ - bit proxy_connect_closed:1; /* TRUE if a proxy disconnected the connection - in a CONNECT request with auth, so that - libcurl should reconnect and continue. */ - bit bound:1; /* set true if bind() has already been done on this socket/ - connection */ - bit type_set:1; /* type= was used in the URL */ - bit multiplex:1; /* connection is multiplexed */ - bit tcp_fastopen:1; /* use TCP Fast Open */ - bit tls_enable_npn:1; /* TLS NPN extension? */ - bit tls_enable_alpn:1; /* TLS ALPN extension? */ - bit socksproxy_connecting:1; /* connecting through a socks proxy */ - bit connect_only:1; + BIT(netrc); /* name+password provided by netrc */ + BIT(bound); /* set true if bind() has already been done on this socket/ + connection */ + BIT(multiplex); /* connection is multiplexed */ + BIT(tcp_fastopen); /* use TCP Fast Open */ + BIT(tls_enable_npn); /* TLS NPN extension? */ + BIT(tls_enable_alpn); /* TLS ALPN extension? */ + BIT(connect_only); + BIT(doh); +#ifdef USE_UNIX_SOCKETS + BIT(abstract_unix_socket); +#endif + BIT(tls_upgraded); + BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with + accept() */ + BIT(parallel_connect); /* set TRUE when a parallel connect attempt has + started (happy eyeballs) */ }; struct hostname { @@ -491,7 +522,7 @@ struct Curl_async { struct Curl_dns_entry *dns; int status; /* if done is TRUE, this is the status from the callback */ void *os_specific; /* 'struct thread_data' for Windows */ - bit done:1; /* set TRUE when the lookup is complete */ + BIT(done); /* set TRUE when the lookup is complete */ }; #define FIRSTSOCKET 0 @@ -518,9 +549,22 @@ enum upgrade101 { UPGR101_WORKING /* talking upgraded protocol */ }; -struct dohresponse { - unsigned char *memory; - size_t size; +enum doh_slots { + /* Explicit values for first two symbols so as to match hard-coded + * constants in existing code + */ + DOH_PROBE_SLOT_IPADDR_V4 = 0, /* make 'V4' stand out for readability */ + DOH_PROBE_SLOT_IPADDR_V6 = 1, /* 'V6' likewise */ + + /* Space here for (possibly build-specific) additional slot definitions */ + + /* for example */ + /* #ifdef WANT_DOH_FOOBAR_TXT */ + /* DOH_PROBE_SLOT_FOOBAR_TXT, */ + /* #endif */ + + /* AFTER all slot definitions, establish how many we have */ + DOH_PROBE_SLOTS }; /* one of these for each DoH request */ @@ -529,12 +573,12 @@ struct dnsprobe { int dnstype; unsigned char dohbuffer[512]; size_t dohlen; - struct dohresponse serverdoh; + struct dynbuf serverdoh; }; struct dohdata { struct curl_slist *headers; - struct dnsprobe probe[2]; + struct dnsprobe probe[DOH_PROBE_SLOTS]; unsigned int pending; /* still outstanding requests */ const char *host; int port; @@ -573,12 +617,7 @@ struct SingleRequest { written as body */ int headerline; /* counts header lines to better track the first one */ - char *hbufp; /* points at *end* of header line */ - size_t hbuflen; char *str; /* within buf */ - char *str_start; /* within buf */ - char *end_ptr; /* within buf */ - char *p; /* within headerbuff */ curl_off_t offset; /* possible resume offset read from the Content-Range: header */ int httpcode; /* error code from the 'HTTP/1.? XXX' or @@ -587,11 +626,10 @@ struct SingleRequest { enum expect100 exp100; /* expect 100 continue state */ enum upgrade101 upgr101; /* 101 upgrade state */ - struct contenc_writer_s *writer_stack; /* Content unencoding stack. */ - /* See sec 3.5, RFC2616. */ + /* Content unencoding stack. See sec 3.5, RFC2616. */ + struct contenc_writer *writer_stack; time_t timeofdoc; long bodywrites; - char *buf; int keepon; char *location; /* This points to an allocated version of the Location: header data */ @@ -612,20 +650,20 @@ struct SingleRequest { #ifndef CURL_DISABLE_DOH struct dohdata doh; /* DoH specific data for this request */ #endif - bit header:1; /* incoming data has HTTP header */ - bit content_range:1; /* set TRUE if Content-Range: was found */ - bit upload_done:1; /* set to TRUE when doing chunked transfer-encoding - upload and we're uploading the last chunk */ - bit ignorebody:1; /* we read a response-body but we ignore it! */ - bit ignorecl:1; /* This HTTP response has no body so we ignore the - Content-Length: header */ - bit chunk:1; /* if set, this is a chunked transfer-encoding */ - bit upload_chunky:1; /* set TRUE if we are doing chunked transfer-encoding - on upload */ - bit getheader:1; /* TRUE if header parsing is wanted */ - bit forbidchunk:1; /* used only to explicitly forbid chunk-upload for - specific upload buffers. See readmoredata() in http.c - for details. */ + BIT(header); /* incoming data has HTTP header */ + BIT(content_range); /* set TRUE if Content-Range: was found */ + BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding + upload and we're uploading the last chunk */ + 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 */ + BIT(chunk); /* if set, this is a chunked transfer-encoding */ + BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding + on upload */ + BIT(getheader); /* TRUE if header parsing is wanted */ + BIT(forbidchunk); /* used only to explicitly forbid chunk-upload for + specific upload buffers. See readmoredata() in http.c + for details. */ }; /* @@ -663,27 +701,23 @@ struct Curl_handler { /* Called from the multi interface during the PROTOCONNECT phase, and it should then return a proper fd set */ int (*proto_getsock)(struct connectdata *conn, - curl_socket_t *socks, - int numsocks); + curl_socket_t *socks); /* Called from the multi interface during the DOING phase, and it should then return a proper fd set */ int (*doing_getsock)(struct connectdata *conn, - curl_socket_t *socks, - int numsocks); + curl_socket_t *socks); /* Called from the multi interface during the DO_MORE phase, and it should then return a proper fd set */ int (*domore_getsock)(struct connectdata *conn, - curl_socket_t *socks, - int numsocks); + curl_socket_t *socks); /* 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. */ int (*perform_getsock)(const struct connectdata *conn, - curl_socket_t *socks, - int numsocks); + curl_socket_t *socks); /* 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 @@ -733,6 +767,8 @@ struct Curl_handler { 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 + user name and password */ #define CONNCHECK_NONE 0 /* No checks */ #define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */ @@ -763,23 +799,55 @@ struct proxy_info { char *passwd; /* proxy password string, allocated */ }; -#define CONNECT_BUFFER_SIZE 16384 - /* struct for HTTP CONNECT state data */ struct http_connect_state { - char connect_buffer[CONNECT_BUFFER_SIZE]; - int perline; /* count bytes per line */ + struct dynbuf rcvbuf; int keepon; - char *line_start; - char *ptr; /* where to store more data */ curl_off_t cl; /* size of content to read and ignore */ enum { TUNNEL_INIT, /* init/default/no tunnel state */ TUNNEL_CONNECT, /* CONNECT has been sent off */ TUNNEL_COMPLETE /* CONNECT response received completely */ } tunnel_state; - bit chunked_encoding:1; - bit close_connection:1; + BIT(chunked_encoding); + BIT(close_connection); +}; + +struct ldapconninfo; + +/* for the (SOCKS) connect state machine */ +enum connect_t { + CONNECT_INIT, + CONNECT_SOCKS_INIT, /* 1 */ + CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */ + CONNECT_SOCKS_READ_INIT, /* 3 set up read */ + CONNECT_SOCKS_READ, /* 4 read server response */ + CONNECT_GSSAPI_INIT, /* 5 */ + CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */ + CONNECT_AUTH_SEND, /* 7 send auth */ + CONNECT_AUTH_READ, /* 8 read auth response */ + CONNECT_REQ_INIT, /* 9 init SOCKS "request" */ + CONNECT_RESOLVING, /* 10 */ + CONNECT_RESOLVED, /* 11 */ + CONNECT_RESOLVE_REMOTE, /* 12 */ + CONNECT_REQ_SEND, /* 13 */ + CONNECT_REQ_SENDING, /* 14 */ + CONNECT_REQ_READ, /* 15 */ + CONNECT_REQ_READ_MORE, /* 16 */ + CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */ +}; + +#define SOCKS_STATE(x) (((x) >= CONNECT_SOCKS_INIT) && \ + ((x) < CONNECT_DONE)) +#define SOCKS_REQUEST_BUFSIZE 600 /* room for large user/pw (255 max each) */ + +struct connstate { + enum connect_t state; + unsigned char socksreq[SOCKS_REQUEST_BUFSIZE]; + + /* CONNECT_SOCKS_SEND */ + ssize_t outstanding; /* send this many bytes more */ + unsigned char *outp; /* send from this pointer */ }; /* @@ -791,7 +859,7 @@ struct connectdata { caution that this might very well vary between different times this connection is used! */ struct Curl_easy *data; - + struct connstate cnnct; struct curl_llist_element bundle_node; /* conncache */ /* chunk is for HTTP chunked encoding, but is in the general connectdata @@ -814,15 +882,15 @@ struct connectdata { /* 'dns_entry' is the particular host we use. This points to an entry in the DNS cache and it will not get pruned while locked. It gets unlocked in - Curl_done(). This entry will be NULL if the connection is re-used as then + multi_done(). This entry will be NULL if the connection is re-used as then there is no name resolve done. */ struct Curl_dns_entry *dns_entry; /* 'ip_addr' is the particular IP we connected to. It points to a struct within the DNS cache, so this pointer is only valid as long as the DNS - cache entry remains locked. It gets unlocked in Curl_done() */ - Curl_addrinfo *ip_addr; - Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */ + cache entry remains locked. It gets unlocked in multi_done() */ + struct Curl_addrinfo *ip_addr; + struct Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */ /* 'ip_addr_str' is the ip_addr data as a human readable string. It remains available as long as the connection does, which is longer than @@ -831,17 +899,26 @@ struct connectdata { unsigned int scope_id; /* Scope id for IPv6 */ - int socktype; /* SOCK_STREAM or SOCK_DGRAM */ + enum { + TRNSPRT_TCP = 3, + TRNSPRT_UDP = 4, + TRNSPRT_QUIC = 5 + } transport; + +#ifdef ENABLE_QUIC + struct quicsocket hequic[2]; /* two, for happy eyeballs! */ + struct quicsocket *quic; +#endif struct hostname host; char *hostname_resolve; /* host name to resolve to address, allocated */ char *secondaryhostname; /* secondary socket host name (ftp) */ struct hostname conn_to_host; /* the host to connect to. valid only if bits.conn_to_host is set */ - +#ifndef CURL_DISABLE_PROXY struct proxy_info socks_proxy; struct proxy_info http_proxy; - +#endif long port; /* which port to use locally */ int remote_port; /* the remote port, not the proxy port! */ int conn_to_port; /* the remote port to connect to. valid only if @@ -870,7 +947,7 @@ struct connectdata { char *passwd; /* password string, allocated */ char *options; /* options string, allocated */ - char *oauth_bearer; /* bearer token for OAuth 2.0, allocated */ + char *sasl_authzid; /* authorisation identity string, allocated */ int httpversion; /* the HTTP version*10 reported by the server */ int rtspversion; /* the RTSP version*10 reported by the server */ @@ -881,8 +958,7 @@ struct connectdata { curl_socket_t sock[2]; /* two sockets, the second is used for the data transfer when doing FTP */ curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */ - bool sock_accepted[2]; /* TRUE if the socket on this index was created with - accept() */ + int tempfamily[2]; /* family used for the temp sockets */ Curl_recv *recv[2]; Curl_send *send[2]; @@ -890,12 +966,16 @@ struct connectdata { struct postponed_data postponed[2]; /* two buffers for two sockets */ #endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ struct ssl_connect_data ssl[2]; /* this is for ssl-stuff */ +#ifndef CURL_DISABLE_PROXY struct ssl_connect_data proxy_ssl[2]; /* this is for proxy ssl-stuff */ +#endif #ifdef USE_SSL void *ssl_extra; /* separately allocated backend-specific data */ #endif struct ssl_primary_config ssl_config; +#ifndef CURL_DISABLE_PROXY struct ssl_primary_config proxy_ssl_config; +#endif struct ConnectBits bits; /* various state-flags for this connection */ /* connecttime: when connect() is called on the current IP address. Used to @@ -904,8 +984,10 @@ struct connectdata { struct curltime connecttime; /* The two fields below get set in Curl_connecthost */ int num_addr; /* number of addresses to try to connect to */ - time_t timeoutms_per_addr; /* how long time in milliseconds to spend on - trying to connect to each IP address */ + + /* how long time in milliseconds to spend on trying to connect to each IP + address, per family */ + timediff_t timeoutms_per_addr[2]; const struct Curl_handler *handler; /* Connection's protocol handler */ const struct Curl_handler *given; /* The protocol first given */ @@ -926,23 +1008,8 @@ struct connectdata { well be the same we read from. CURL_SOCKET_BAD disables */ - /** Dynamically allocated strings, MUST be freed before this **/ - /** struct is killed. **/ - struct dynamically_allocated_data { - char *proxyuserpwd; - char *uagent; - char *accept_encoding; - char *userpwd; - char *rangeline; - char *ref; - char *host; - char *cookiehost; - char *rtsp_transport; - char *te; /* TE: request header */ - } allocptr; - #ifdef HAVE_GSSAPI - bit sec_complete:1; /* if Kerberos is enabled for this connection */ + BIT(sec_complete); /* if Kerberos is enabled for this connection */ enum protection_level command_prot; enum protection_level data_prot; enum protection_level request_data_prot; @@ -974,14 +1041,6 @@ struct connectdata { because it authenticates connections, not single requests! */ struct ntlmdata proxyntlm; /* NTLM data for proxy */ - -#if defined(NTLM_WB_ENABLED) - /* used for communication with Samba's winbind daemon helper ntlm_auth */ - curl_socket_t ntlm_auth_hlpr_socket; - pid_t ntlm_auth_hlpr_pid; - char *challenge_header; - char *response_header; -#endif #endif #ifdef USE_SPNEGO @@ -995,10 +1054,8 @@ struct connectdata { /* data used for the asynch name resolve callback */ struct Curl_async async; - /* These three are used for chunked-encoding trailer support */ - char *trailer; /* allocated buffer to store trailer in */ - int trlMax; /* allocated buffer size */ - int trlPos; /* index of where to store data */ + /* for chunked-encoded trailer */ + struct dynbuf trailer; union { struct ftp_conn ftpc; @@ -1010,7 +1067,9 @@ struct connectdata { struct smtp_conn smtpc; struct rtsp_conn rtspc; struct smb_conn smbc; - void *generic; /* RTMP and LDAP use this */ + void *rtmp; + struct ldapconninfo *ldapc; + struct mqtt_conn mqtt; } proto; int cselect_bits; /* bitmask of socket events */ @@ -1031,19 +1090,10 @@ struct connectdata { struct http_connect_state *connect_state; /* for HTTP CONNECT */ struct connectbundle *bundle; /* The bundle we are member of */ int negnpn; /* APLN or NPN TLS negotiated protocol, CURL_HTTP_VERSION* */ - + int retrycount; /* number of retries on a new connection */ #ifdef USE_UNIX_SOCKETS char *unix_domain_socket; - bit abstract_unix_socket:1; #endif - bit tls_upgraded:1; - /* the two following *_inuse fields are only flags, not counters in any way. - If TRUE it means the channel is in use, and if FALSE it means the channel - is up for grabs by one. */ - bit readchannel_inuse:1; /* whether the read channel is in use by an easy - handle */ - bit writechannel_inuse:1; /* whether the write channel is in use by an easy - handle */ }; /* The end of connectdata. */ @@ -1065,6 +1115,7 @@ struct PureInfo { long numconnects; /* how many new connection did libcurl created */ char *contenttype; /* the content type of the object */ char *wouldredirect; /* URL this would've been redirected to if asked to */ + curl_off_t retry_after; /* info from Retry-After: header */ /* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip' and, 'conn_local_port' are copied over from the connectdata struct in @@ -1081,11 +1132,11 @@ struct PureInfo { const char *conn_scheme; unsigned int conn_protocol; struct curl_certinfo certs; /* info about the certs, only populated in - OpenSSL builds. Asked for with - CURLOPT_CERTINFO / CURLINFO_CERTINFO */ - - bit timecond:1; /* set to TRUE if the time condition didn't match, which - thus made the document NOT get fetched */ + OpenSSL, GnuTLS, Schannel, NSS and GSKit + builds. Asked for with CURLOPT_CERTINFO + / CURLINFO_CERTINFO */ + BIT(timecond); /* set to TRUE if the time condition didn't match, which + thus made the document NOT get fetched */ }; @@ -1102,17 +1153,17 @@ struct Progress { int width; /* screen width at download start */ int flags; /* see progress.h */ - time_t timespent; + timediff_t timespent; curl_off_t dlspeed; curl_off_t ulspeed; - time_t t_nslookup; - time_t t_connect; - time_t t_appconnect; - time_t t_pretransfer; - time_t t_starttransfer; - time_t t_redirect; + timediff_t t_nslookup; + timediff_t t_connect; + timediff_t t_appconnect; + timediff_t t_pretransfer; + timediff_t t_starttransfer; + timediff_t t_redirect; struct curltime start; struct curltime t_startsingle; @@ -1132,8 +1183,8 @@ struct Progress { curl_off_t speeder[ CURR_TIME ]; struct curltime speeder_time[ CURR_TIME ]; int speeder_c; - bit callback:1; /* set when progress callback is used */ - bit is_t_startransfer_set:1; + BIT(callback); /* set when progress callback is used */ + BIT(is_t_startransfer_set); }; typedef enum { @@ -1164,29 +1215,18 @@ typedef enum { RTSPREQ_LAST /* last in list */ } Curl_RtspReq; -/* - * Values that are generated, temporary or calculated internally for a - * "session handle" must be defined within the 'struct UrlState'. This struct - * will be used within the Curl_easy struct. When the 'Curl_easy' - * struct is cloned, this data MUST NOT be copied. - * - * Remember that any "state" information goes globally for the curl handle. - * Session-data MUST be put in the connectdata struct and here. */ -#define MAX_CURL_USER_LENGTH 256 -#define MAX_CURL_PASSWORD_LENGTH 256 - struct auth { unsigned long want; /* Bitmask set to the authentication methods wanted by app (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */ unsigned long picked; unsigned long avail; /* Bitmask for what the server reports to support for this resource */ - bit done:1; /* TRUE when the auth phase is done and ready to do the - *actual* request */ - bit multipass:1; /* TRUE if this is not yet authenticated but within the - auth multipass negotiation */ - bit iestyle:1; /* TRUE if digest should be done IE-style or FALSE if it - should be RFC compliant */ + BIT(done); /* TRUE when the auth phase is done and ready to do the + actual request */ + BIT(multipass); /* TRUE if this is not yet authenticated but within the + auth multipass negotiation */ + BIT(iestyle); /* TRUE if digest should be done IE-style or FALSE if it + should be RFC compliant */ }; struct Curl_http2_dep { @@ -1200,9 +1240,7 @@ struct Curl_http2_dep { * BODY). */ struct tempbuf { - char *buf; /* allocated buffer to keep data in when a write callback - returns to make the connection paused */ - size_t len; /* size of the 'tempwrite' allocated buffer */ + struct dynbuf b; int type; /* type of the 'tempwrite' buffer as a bitmask that is used with Curl_client_write() */ }; @@ -1212,7 +1250,8 @@ typedef enum { EXPIRE_100_TIMEOUT, EXPIRE_ASYNC_NAME, EXPIRE_CONNECTTIMEOUT, - EXPIRE_DNS_PER_NAME, + EXPIRE_DNS_PER_NAME, /* family1 */ + EXPIRE_DNS_PER_NAME2, /* family2 */ EXPIRE_HAPPY_EYEBALLS_DNS, /* See asyn-ares.c */ EXPIRE_HAPPY_EYEBALLS, EXPIRE_MULTI_PENDING, @@ -1220,6 +1259,7 @@ typedef enum { EXPIRE_SPEEDCHECK, EXPIRE_TIMEOUT, EXPIRE_TOOFAST, + EXPIRE_QUIC, EXPIRE_LAST /* not an actual timer, used as a marker only */ } expire_id; @@ -1254,7 +1294,6 @@ struct urlpieces { }; struct UrlState { - /* Points to the connection cache */ struct conncache *conn_cache; @@ -1262,9 +1301,7 @@ struct UrlState { struct curltime keeps_speed; /* for the progress meter really */ struct connectdata *lastconnect; /* The last connection, NULL if undefined */ - - char *headerbuff; /* allocated buffer to store headers in */ - size_t headersize; /* size of the allocation */ + struct dynbuf headerb; /* buffer to store headers in */ char *buffer; /* download buffer */ char *ulbuf; /* allocated upload buffer or NULL */ @@ -1315,7 +1352,7 @@ struct UrlState { /* do FTP line-end conversions on most platforms */ #define CURL_DO_LINEEND_CONV /* for FTP downloads: track CRLF sequences that span blocks */ - bit prev_block_had_trailing_cr:1; + BIT(prev_block_had_trailing_cr); /* for FTP downloads: how many CRLFs did we converted to LFs? */ curl_off_t crlf_conversions; #endif @@ -1342,40 +1379,60 @@ struct UrlState { int stream_weight; CURLU *uh; /* URL handle for the current parsed URL */ struct urlpieces up; + Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */ #ifndef CURL_DISABLE_HTTP size_t trailers_bytes_sent; - Curl_send_buffer *trailers_buf; /* a buffer containing the compiled trailing - headers */ + struct dynbuf trailers_buf; /* a buffer containing the compiled trailing + headers */ #endif trailers_state trailers_state; /* whether we are sending trailers and what stage are we at */ + + /* Dynamically allocated strings, MUST be freed before this struct is + killed. */ + struct dynamically_allocated_data { + char *proxyuserpwd; + char *uagent; + char *accept_encoding; + char *userpwd; + char *rangeline; + char *ref; + char *host; + char *cookiehost; + char *rtsp_transport; + char *te; /* TE: request header */ + } aptr; + #ifdef CURLDEBUG - bit conncache_lock:1; + BIT(conncache_lock); #endif /* when curl_easy_perform() is called, the multi handle is "owned" by the easy handle so curl_easy_cleanup() on such an easy handle will also close the multi handle! */ - bit multi_owned_by_easy:1; + BIT(multi_owned_by_easy); - bit this_is_a_follow:1; /* this is a followed Location: request */ - bit refused_stream:1; /* this was refused, try again */ - bit errorbuf:1; /* Set to TRUE if the error buffer is already filled in. + BIT(this_is_a_follow); /* this is a followed Location: request */ + BIT(refused_stream); /* this was refused, try again */ + BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in. This must be set to FALSE every time _easy_perform() is called. */ - bit allow_port:1; /* Is set.use_port allowed to take effect or not. This + BIT(allow_port); /* Is set.use_port allowed to take effect or not. This is always set TRUE when curl_easy_perform() is called. */ - bit authproblem:1; /* TRUE if there's some problem authenticating */ + BIT(authproblem); /* TRUE if there's some problem authenticating */ /* set after initial USER failure, to prevent an authentication loop */ - bit ftp_trying_alternative:1; - bit wildcardmatch:1; /* enable wildcard matching */ - bit expect100header:1; /* TRUE if we added Expect: 100-continue */ - bit use_range:1; - bit rangestringalloc:1; /* the range string is malloc()'ed */ - bit done:1; /* set to FALSE when Curl_init_do() is called and set to TRUE + BIT(ftp_trying_alternative); + BIT(wildcardmatch); /* enable wildcard matching */ + BIT(expect100header); /* TRUE if we added Expect: 100-continue */ + BIT(disableexpect); /* TRUE if Expect: is disabled due to a previous + 417 response */ + BIT(use_range); + BIT(rangestringalloc); /* the range string is malloc()'ed */ + BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE when multi_done() is called, to prevent multi_done() to get invoked twice when the multi interface is used. */ - bit stream_depends_e:1; /* set or don't set the Exclusive bit */ - bit previouslypending:1; /* this transfer WAS in the multi->pending queue */ + BIT(stream_depends_e); /* set or don't set the Exclusive bit */ + BIT(previouslypending); /* this transfer WAS in the multi->pending queue */ + BIT(cookie_engine); }; @@ -1393,9 +1450,9 @@ struct DynamicStatic { curl_easy_setopt(COOKIEFILE) calls */ struct curl_slist *resolve; /* set to point to the set.resolve list when this should be dealt with in pretransfer */ - bit url_alloc:1; /* URL string is malloc()'ed */ - bit referer_alloc:1; /* referer string is malloc()ed */ - bit wildcard_resolve:1; /* Set to true if any resolve change is a + BIT(url_alloc); /* URL string is malloc()'ed */ + BIT(referer_alloc); /* referer string is malloc()ed */ + BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */ }; @@ -1410,6 +1467,14 @@ struct DynamicStatic { struct Curl_multi; /* declared and used only in multi.c */ +/* + * This enumeration MUST not use conditional directives (#ifdefs), new + * null terminated strings MUST be added to the enumeration immediately + * before STRING_LASTZEROTERMINATED, binary fields immediately before + * STRING_LAST. When doing so, ensure that the packages/OS400/chkstring.c + * test is updated and applicable changes for EBCDIC to ASCII conversion + * are catered for in curl_easy_setopt_ccsid() + */ enum dupstring { STRING_CERT_ORIG, /* client certificate file name */ STRING_CERT_PROXY, /* client certificate file name */ @@ -1466,33 +1531,41 @@ enum dupstring { STRING_RTSP_SESSION_ID, /* Session ID to use */ STRING_RTSP_STREAM_URI, /* Stream URI for this request */ STRING_RTSP_TRANSPORT, /* Transport for this session */ -#ifdef USE_SSH + STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */ STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */ STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */ STRING_SSH_KNOWNHOSTS, /* file name of knownhosts file */ -#endif + STRING_PROXY_SERVICE_NAME, /* Proxy service name */ STRING_SERVICE_NAME, /* Service name */ STRING_MAIL_FROM, STRING_MAIL_AUTH, -#ifdef USE_TLS_SRP STRING_TLSAUTH_USERNAME_ORIG, /* TLS auth */ STRING_TLSAUTH_USERNAME_PROXY, /* TLS auth */ STRING_TLSAUTH_PASSWORD_ORIG, /* TLS auth */ STRING_TLSAUTH_PASSWORD_PROXY, /* TLS auth */ -#endif + STRING_BEARER, /* , if used */ -#ifdef USE_UNIX_SOCKETS + STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */ -#endif + STRING_TARGET, /* CURLOPT_REQUEST_TARGET */ STRING_DOH, /* CURLOPT_DOH_URL */ -#ifdef USE_ALTSVC + STRING_ALTSVC, /* CURLOPT_ALTSVC */ -#endif - /* -- end of zero-terminated strings -- */ + + STRING_SASL_AUTHZID, /* CURLOPT_SASL_AUTHZID */ + + STRING_TEMP_URL, /* temp URL storage for proxy use */ + + STRING_DNS_SERVERS, + STRING_DNS_INTERFACE, + STRING_DNS_LOCAL_IP4, + STRING_DNS_LOCAL_IP6, + + /* -- end of null-terminated strings -- */ STRING_LASTZEROTERMINATED, @@ -1500,9 +1573,20 @@ enum dupstring { STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ + STRING_LAST /* not used, just an end-of-list marker */ }; +enum dupblob { + BLOB_CERT_ORIG, + BLOB_CERT_PROXY, + BLOB_KEY_ORIG, + BLOB_KEY_PROXY, + BLOB_SSL_ISSUERCERT_ORIG, + BLOB_SSL_ISSUERCERT_PROXY, + BLOB_LAST +}; + /* callback that gets called when this easy handle is completed within a multi handle. Only used for internally created transfers, like for example DoH. */ @@ -1600,11 +1684,13 @@ struct UserDefined { the hostname and port to connect to */ curl_TimeCond timecondition; /* kind of time/date comparison */ time_t timevalue; /* what time to compare with */ - Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */ + Curl_HttpReq method; /* what kind of HTTP request (if any) is this */ long httpversion; /* when non-zero, a specific HTTP version requested to be used in the library's request(s) */ struct ssl_config_data ssl; /* user defined SSL stuff */ +#ifndef CURL_DISABLE_PROXY struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */ +#endif struct ssl_general_config general_ssl; /* general user defined SSL stuff */ curl_proxytype proxytype; /* what kind of proxy that is in use */ long dns_cache_timeout; /* DNS cache timeout */ @@ -1634,6 +1720,7 @@ struct UserDefined { long new_directory_perms; /* Permissions to use when creating remote dirs */ long ssh_auth_types; /* allowed SSH auth types */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ + struct curl_blob *blobs[BLOB_LAST]; unsigned int scope_id; /* Scope id for IPv6 */ long allowed_protocols; long redir_protocols; @@ -1671,84 +1758,84 @@ struct UserDefined { CURLU *uh; /* URL handle for the current parsed URL */ void *trailer_data; /* pointer to pass to trailer data callback */ curl_trailer_callback trailer_callback; /* trailing data callback */ - bit is_fread_set:1; /* has read callback been set to non-NULL? */ - bit is_fwrite_set:1; /* has write callback been set to non-NULL? */ - bit free_referer:1; /* set TRUE if 'referer' points to a string we + BIT(is_fread_set); /* has read callback been set to non-NULL? */ + BIT(is_fwrite_set); /* has write callback been set to non-NULL? */ + BIT(free_referer); /* set TRUE if 'referer' points to a string we allocated */ - bit tftp_no_options:1; /* do not send TFTP options requests */ - bit sep_headers:1; /* handle host and proxy headers separately */ - bit cookiesession:1; /* new cookie session? */ - bit crlf:1; /* convert crlf on ftp upload(?) */ - bit strip_path_slash:1; /* strip off initial slash from path */ - bit ssh_compression:1; /* enable SSH compression */ + BIT(tftp_no_options); /* do not send TFTP options requests */ + BIT(sep_headers); /* handle host and proxy headers separately */ + BIT(cookiesession); /* new cookie session? */ + BIT(crlf); /* convert crlf on ftp upload(?) */ + BIT(strip_path_slash); /* strip off initial slash from path */ + BIT(ssh_compression); /* enable SSH compression */ /* Here follows boolean settings that define how to behave during this session. They are STATIC, set by libcurl users or at least initially and they don't change during operations. */ - bit get_filetime:1; /* get the time and get of the remote file */ - bit tunnel_thru_httpproxy:1; /* use CONNECT through a HTTP proxy */ - bit prefer_ascii:1; /* ASCII rather than binary */ - bit ftp_append:1; /* append, not overwrite, on upload */ - bit ftp_list_only:1; /* switch FTP command for listing directories */ + BIT(get_filetime); /* get the time and get of the remote file */ + BIT(tunnel_thru_httpproxy); /* use CONNECT through a HTTP proxy */ + BIT(prefer_ascii); /* ASCII rather than binary */ + BIT(ftp_append); /* append, not overwrite, on upload */ + BIT(ftp_list_only); /* switch FTP command for listing directories */ #ifndef CURL_DISABLE_FTP - bit ftp_use_port:1; /* use the FTP PORT command */ - bit ftp_use_epsv:1; /* if EPSV is to be attempted or not */ - bit ftp_use_eprt:1; /* if EPRT is to be attempted or not */ - bit ftp_use_pret:1; /* if PRET is to be used before PASV or not */ - bit ftp_skip_ip:1; /* skip the IP address the FTP server passes on to + BIT(ftp_use_port); /* use the FTP PORT command */ + BIT(ftp_use_epsv); /* if EPSV is to be attempted or not */ + BIT(ftp_use_eprt); /* if EPRT is to be attempted or not */ + BIT(ftp_use_pret); /* if PRET is to be used before PASV or not */ + BIT(ftp_skip_ip); /* skip the IP address the FTP server passes on to us */ #endif - bit hide_progress:1; /* don't use the progress meter */ - bit http_fail_on_error:1; /* fail on HTTP error codes >= 400 */ - bit http_keep_sending_on_error:1; /* for HTTP status codes >= 300 */ - bit http_follow_location:1; /* follow HTTP redirects */ - bit http_transfer_encoding:1; /* request compressed HTTP - transfer-encoding */ - bit allow_auth_to_other_hosts:1; - bit include_header:1; /* include received protocol headers in data output */ - bit http_set_referer:1; /* is a custom referer used */ - bit http_auto_referer:1; /* set "correct" referer when following - location: */ - bit opt_no_body:1; /* as set with CURLOPT_NOBODY */ - bit upload:1; /* upload request */ - bit verbose:1; /* output verbosity */ - bit krb:1; /* Kerberos connection requested */ - bit reuse_forbid:1; /* forbidden to be reused, close after use */ - bit reuse_fresh:1; /* do not re-use an existing connection */ - - bit no_signal:1; /* do not use any signal/alarm handler */ - bit tcp_nodelay:1; /* whether to enable TCP_NODELAY or not */ - bit ignorecl:1; /* ignore content length */ - bit connect_only:1; /* make connection, let application use the socket */ - bit http_te_skip:1; /* pass the raw body data to the user, even when - transfer-encoded (chunked, compressed) */ - bit http_ce_skip:1; /* pass the raw body data to the user, even when - content-encoded (chunked, compressed) */ - bit proxy_transfer_mode:1; /* set transfer mode (;type=) when doing - FTP via an HTTP proxy */ + BIT(hide_progress); /* don't use the progress meter */ + BIT(http_fail_on_error); /* fail on HTTP error codes >= 400 */ + BIT(http_keep_sending_on_error); /* for HTTP status codes >= 300 */ + BIT(http_follow_location); /* follow HTTP redirects */ + BIT(http_transfer_encoding); /* request compressed HTTP transfer-encoding */ + BIT(allow_auth_to_other_hosts); + BIT(include_header); /* include received protocol headers in data output */ + BIT(http_set_referer); /* is a custom referer used */ + BIT(http_auto_referer); /* set "correct" referer when following + location: */ + BIT(opt_no_body); /* as set with CURLOPT_NOBODY */ + BIT(upload); /* upload request */ + BIT(verbose); /* output verbosity */ + BIT(krb); /* Kerberos connection requested */ + BIT(reuse_forbid); /* forbidden to be reused, close after use */ + BIT(reuse_fresh); /* do not re-use an existing connection */ + BIT(no_signal); /* do not use any signal/alarm handler */ + BIT(tcp_nodelay); /* whether to enable TCP_NODELAY or not */ + BIT(ignorecl); /* ignore content length */ + BIT(connect_only); /* make connection, let application use the socket */ + BIT(http_te_skip); /* pass the raw body data to the user, even when + transfer-encoded (chunked, compressed) */ + BIT(http_ce_skip); /* pass the raw body data to the user, even when + content-encoded (chunked, compressed) */ + BIT(proxy_transfer_mode); /* set transfer mode (;type=) when doing + FTP via an HTTP proxy */ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - bit socks5_gssapi_nec:1; /* Flag to support NEC SOCKS5 server */ + BIT(socks5_gssapi_nec); /* Flag to support NEC SOCKS5 server */ #endif - bit sasl_ir:1; /* Enable/disable SASL initial response */ - bit wildcard_enabled:1; /* enable wildcard matching */ - bit tcp_keepalive:1; /* use TCP keepalives */ - bit tcp_fastopen:1; /* use TCP Fast Open */ - bit ssl_enable_npn:1; /* TLS NPN extension? */ - bit ssl_enable_alpn:1;/* TLS ALPN extension? */ - bit path_as_is:1; /* allow dotdots? */ - bit pipewait:1; /* wait for multiplex status before starting a new - connection */ - bit suppress_connect_headers:1; /* suppress proxy CONNECT response headers - from user callbacks */ - bit dns_shuffle_addresses:1; /* whether to shuffle addresses before use */ - bit stream_depends_e:1; /* set or don't set the Exclusive bit */ - bit haproxyprotocol:1; /* whether to send HAProxy PROXY protocol v1 - header */ - bit abstract_unix_socket:1; - bit disallow_username_in_url:1; /* disallow username in url */ - bit doh:1; /* DNS-over-HTTPS enabled */ - bit doh_get:1; /* use GET for DoH requests, instead of POST */ - bit http09_allowed:1; /* allow HTTP/0.9 responses */ + BIT(sasl_ir); /* Enable/disable SASL initial response */ + BIT(wildcard_enabled); /* enable wildcard matching */ + BIT(tcp_keepalive); /* use TCP keepalives */ + BIT(tcp_fastopen); /* use TCP Fast Open */ + BIT(ssl_enable_npn); /* TLS NPN extension? */ + BIT(ssl_enable_alpn);/* TLS ALPN extension? */ + BIT(path_as_is); /* allow dotdots? */ + BIT(pipewait); /* wait for multiplex status before starting a new + connection */ + BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers + from user callbacks */ + BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */ + BIT(stream_depends_e); /* set or don't set the Exclusive bit */ + BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1 + header */ + BIT(abstract_unix_socket); + BIT(disallow_username_in_url); /* disallow username in url */ + BIT(doh); /* DNS-over-HTTPS enabled */ + BIT(doh_get); /* use GET for DoH requests, instead of POST */ + BIT(http09_allowed); /* allow HTTP/0.9 responses */ + BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some + recipients */ }; struct Names { @@ -1777,7 +1864,6 @@ struct Curl_easy { struct connectdata *conn; struct curl_llist_element connect_queue; - struct curl_llist_element sh_queue; /* list per Curl_sh_entry */ struct curl_llist_element conn_queue; /* list per connectdata */ CURLMstate mstate; /* the handle's state */ diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c index 6f452c16942..3a5c9430f48 100644 --- a/Utilities/cmcurl/lib/vauth/cleartext.c +++ b/Utilities/cmcurl/lib/vauth/cleartext.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -81,7 +81,8 @@ CURLcode Curl_auth_create_plain_message(struct Curl_easy *data, plen = strlen(passwd); /* Compute binary message length. Check for overflows. */ - if(((zlen + clen) > SIZE_T_MAX/4) || (plen > (SIZE_T_MAX/2 - 2))) + if((zlen > SIZE_T_MAX/4) || (clen > SIZE_T_MAX/4) || + (plen > (SIZE_T_MAX/2 - 2))) return CURLE_OUT_OF_MEMORY; plainlen = zlen + clen + plen + 2; diff --git a/Utilities/cmcurl/lib/vauth/cram.c b/Utilities/cmcurl/lib/vauth/cram.c index d148618b0d7..717d7f093df 100644 --- a/Utilities/cmcurl/lib/vauth/cram.c +++ b/Utilities/cmcurl/lib/vauth/cram.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -96,7 +96,7 @@ CURLcode Curl_auth_create_cram_md5_message(struct Curl_easy *data, { CURLcode result = CURLE_OK; size_t chlglen = 0; - HMAC_context *ctxt; + struct HMAC_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; char *response; diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index f9cdc9dd006..b9210a8fec3 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -62,7 +62,7 @@ what ultimately goes over the network. */ #define CURL_OUTPUT_DIGEST_CONV(a, b) \ - result = Curl_convert_to_network(a, (char *)b, strlen((const char *)b)); \ + result = Curl_convert_to_network(a, b, strlen(b)); \ if(result) { \ free(b); \ return result; \ @@ -357,9 +357,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const char *service, char **outptr, size_t *outlen) { - CURLcode result = CURLE_OK; size_t i; - MD5_context *ctxt; + struct MD5_context *ctxt; char *response = NULL; unsigned char digest[MD5_DIGEST_LEN]; char HA1_hex[2 * MD5_DIGEST_LEN + 1]; @@ -377,10 +376,12 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, char *spn = NULL; /* Decode the challenge message */ - result = auth_decode_digest_md5_message(chlg64, nonce, sizeof(nonce), - realm, sizeof(realm), - algorithm, sizeof(algorithm), - qop_options, sizeof(qop_options)); + CURLcode result = auth_decode_digest_md5_message(chlg64, nonce, + sizeof(nonce), realm, + sizeof(realm), algorithm, + sizeof(algorithm), + qop_options, + sizeof(qop_options)); if(result) return result; @@ -659,7 +660,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, } /* - * _Curl_auth_create_digest_http_message() + * auth_create_digest_http_message() * * This is used to generate a HTTP DIGEST response message ready for sending * to the recipient. @@ -678,7 +679,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * * Returns CURLE_OK on success. */ -static CURLcode _Curl_auth_create_digest_http_message( +static CURLcode auth_create_digest_http_message( struct Curl_easy *data, const char *userp, const char *passwdp, @@ -687,12 +688,12 @@ static CURLcode _Curl_auth_create_digest_http_message( struct digestdata *digest, char **outptr, size_t *outlen, void (*convert_to_ascii)(unsigned char *, unsigned char *), - void (*hash)(unsigned char *, const unsigned char *)) + void (*hash)(unsigned char *, const unsigned char *, + const size_t)) { CURLcode result; unsigned char hashbuf[32]; /* 32 bytes/256 bits */ unsigned char request_digest[65]; - unsigned char *hashthis; unsigned char ha1[65]; /* 64 digits and 1 zero byte */ unsigned char ha2[65]; /* 64 digits and 1 zero byte */ char userh[65]; @@ -700,6 +701,7 @@ static CURLcode _Curl_auth_create_digest_http_message( size_t cnonce_sz = 0; char *userp_quoted; char *response = NULL; + char *hashthis = NULL; char *tmp = NULL; if(!digest->nc) @@ -721,12 +723,12 @@ static CURLcode _Curl_auth_create_digest_http_message( } if(digest->userhash) { - hashthis = (unsigned char *) aprintf("%s:%s", userp, digest->realm); + hashthis = aprintf("%s:%s", userp, digest->realm); if(!hashthis) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, hashthis); - hash(hashbuf, hashthis); + hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); convert_to_ascii(hashbuf, (unsigned char *)userh); } @@ -742,14 +744,13 @@ static CURLcode _Curl_auth_create_digest_http_message( unq(nonce-value) ":" unq(cnonce-value) */ - hashthis = (unsigned char *) - aprintf("%s:%s:%s", digest->userhash ? userh : userp, - digest->realm, passwdp); + hashthis = aprintf("%s:%s:%s", digest->userhash ? userh : userp, + digest->realm, passwdp); if(!hashthis) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */ - hash(hashbuf, hashthis); + hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); convert_to_ascii(hashbuf, ha1); @@ -762,7 +763,7 @@ static CURLcode _Curl_auth_create_digest_http_message( return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */ - hash(hashbuf, (unsigned char *) tmp); + hash(hashbuf, (unsigned char *) tmp, strlen(tmp)); free(tmp); convert_to_ascii(hashbuf, ha1); } @@ -780,19 +781,19 @@ static CURLcode _Curl_auth_create_digest_http_message( 5.1.1 of RFC 2616) */ - hashthis = (unsigned char *) aprintf("%s:%s", request, uripath); + hashthis = aprintf("%s:%s", request, uripath); if(!hashthis) return CURLE_OUT_OF_MEMORY; if(digest->qop && strcasecompare(digest->qop, "auth-int")) { /* We don't support auth-int for PUT or POST */ char hashed[65]; - unsigned char *hashthis2; + char *hashthis2; - hash(hashbuf, (const unsigned char *)""); + hash(hashbuf, (const unsigned char *)"", 0); convert_to_ascii(hashbuf, (unsigned char *)hashed); - hashthis2 = (unsigned char *)aprintf("%s:%s", hashthis, hashed); + hashthis2 = aprintf("%s:%s", hashthis, hashed); free(hashthis); hashthis = hashthis2; } @@ -801,31 +802,23 @@ static CURLcode _Curl_auth_create_digest_http_message( return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */ - hash(hashbuf, hashthis); + hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); convert_to_ascii(hashbuf, ha2); if(digest->qop) { - hashthis = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s", - ha1, - digest->nonce, - digest->nc, - digest->cnonce, - digest->qop, - ha2); + hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc, + digest->cnonce, digest->qop, ha2); } else { - hashthis = (unsigned char *) aprintf("%s:%s:%s", - ha1, - digest->nonce, - ha2); + hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2); } if(!hashthis) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */ - hash(hashbuf, hashthis); + hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); convert_to_ascii(hashbuf, request_digest); @@ -898,7 +891,7 @@ static CURLcode _Curl_auth_create_digest_http_message( if(digest->algorithm) { /* Append the algorithm */ - tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm); + tmp = aprintf("%s, algorithm=%s", response, digest->algorithm); free(response); if(!tmp) return CURLE_OUT_OF_MEMORY; @@ -954,21 +947,21 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, switch(digest->algo) { case CURLDIGESTALGO_MD5: case CURLDIGESTALGO_MD5SESS: - return _Curl_auth_create_digest_http_message(data, userp, passwdp, - request, uripath, digest, - outptr, outlen, - auth_digest_md5_to_ascii, - Curl_md5it); + return auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + outptr, outlen, + auth_digest_md5_to_ascii, + Curl_md5it); case CURLDIGESTALGO_SHA256: case CURLDIGESTALGO_SHA256SESS: case CURLDIGESTALGO_SHA512_256: case CURLDIGESTALGO_SHA512_256SESS: - return _Curl_auth_create_digest_http_message(data, userp, passwdp, - request, uripath, digest, - outptr, outlen, - auth_digest_sha256_to_ascii, - Curl_sha256it); + return auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + outptr, outlen, + auth_digest_sha256_to_ascii, + Curl_sha256it); default: return CURLE_UNSUPPORTED_PROTOCOL; diff --git a/Utilities/cmcurl/lib/vauth/digest.h b/Utilities/cmcurl/lib/vauth/digest.h index 8686c44a421..cc05fdb7690 100644 --- a/Utilities/cmcurl/lib/vauth/digest.h +++ b/Utilities/cmcurl/lib/vauth/digest.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c index fe8093e8b34..4998306ce81 100644 --- a/Utilities/cmcurl/lib/vauth/digest_sspi.c +++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2014 - 2016, Steve Holme, . - * Copyright (C) 2015 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2015 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -61,6 +61,11 @@ bool Curl_auth_is_digest_supported(void) status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), &SecurityPackage); + /* Release the package buffer as it is not required anymore */ + if(status == SEC_E_OK) { + s_pSecFn->FreeContextBuffer(SecurityPackage); + } + return (status == SEC_E_OK ? TRUE : FALSE); } @@ -129,7 +134,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, if(status != SEC_E_OK) { free(input_token); - return CURLE_NOT_BUILT_IN; + failf(data, "SSPI: couldn't get auth info\n"); + return CURLE_AUTH_ERROR; } token_max = SecurityPackage->cbMaxToken; @@ -220,7 +226,10 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, free(output_token); free(input_token); - return CURLE_RECV_ERROR; + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; } /* Base64 encode the response */ @@ -280,13 +289,13 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg, if(strcasecompare(value, "realm")) { /* Setup identity's domain and length */ - domain.tchar_ptr = Curl_convert_UTF8_to_tchar((char *) content); + domain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *) content); if(!domain.tchar_ptr) return CURLE_OUT_OF_MEMORY; dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr); if(!dup_domain.tchar_ptr) { - Curl_unicodefree(domain.tchar_ptr); + curlx_unicodefree(domain.tchar_ptr); return CURLE_OUT_OF_MEMORY; } @@ -295,7 +304,7 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg, identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr)); dup_domain.tchar_ptr = NULL; - Curl_unicodefree(domain.tchar_ptr); + curlx_unicodefree(domain.tchar_ptr); } else { /* Unknown specifier, ignore it! */ @@ -423,8 +432,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Query the security package for DigestSSP */ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), &SecurityPackage); - if(status != SEC_E_OK) - return CURLE_NOT_BUILT_IN; + if(status != SEC_E_OK) { + failf(data, "SSPI: couldn't get auth info\n"); + return CURLE_AUTH_ERROR; + } token_max = SecurityPackage->cbMaxToken; @@ -572,7 +583,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, resp_buf.pvBuffer = output_token; resp_buf.cbBuffer = curlx_uztoul(token_max); - spn = Curl_convert_UTF8_to_tchar((char *) uripath); + spn = curlx_convert_UTF8_to_tchar((char *) uripath); if(!spn) { s_pSecFn->FreeCredentialsHandle(&credentials); @@ -594,7 +605,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, &chlg_desc, 0, digest->http_context, &resp_desc, &attrs, &expiry); - Curl_unicodefree(spn); + curlx_unicodefree(spn); if(status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) @@ -607,7 +618,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, Curl_safefree(digest->http_context); - return CURLE_OUT_OF_MEMORY; + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; } output_token_len = resp_buf.cbBuffer; diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c index ea0a5f18920..95bab0e2ee9 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c @@ -121,7 +121,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, free(spn); - return CURLE_OUT_OF_MEMORY; + return CURLE_AUTH_ERROR; } free(spn); @@ -168,7 +168,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, Curl_gss_log_error(data, "gss_init_sec_context() failed: ", major_status, minor_status); - return CURLE_RECV_ERROR; + return CURLE_AUTH_ERROR; } if(output_token.value && output_token.length) { @@ -252,7 +252,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, free(chlg); - return CURLE_OUT_OF_MEMORY; + return CURLE_AUTH_ERROR; } /* Convert the username from internal format to a displayable token */ @@ -264,7 +264,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, free(chlg); - return CURLE_OUT_OF_MEMORY; + return CURLE_AUTH_ERROR; } /* Setup the challenge "input" security buffer */ @@ -355,7 +355,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, free(message); - return CURLE_OUT_OF_MEMORY; + return CURLE_AUTH_ERROR; } /* Base64 encode the response */ diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c index 1f6e462bf7b..1fb6257ea97 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2014 - 2019, Steve Holme, . + * Copyright (C) 2014 - 2020, Steve Holme, . * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -58,6 +58,11 @@ bool Curl_auth_is_gssapi_supported(void) TEXT(SP_NAME_KERBEROS), &SecurityPackage); + /* Release the package buffer as it is not required anymore */ + if(status == SEC_E_OK) { + s_pSecFn->FreeContextBuffer(SecurityPackage); + } + return (status == SEC_E_OK ? TRUE : FALSE); } @@ -120,7 +125,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, TEXT(SP_NAME_KERBEROS), &SecurityPackage); if(status != SEC_E_OK) { - return CURLE_NOT_BUILT_IN; + failf(data, "SSPI: couldn't get auth info\n"); + return CURLE_AUTH_ERROR; } krb5->token_max = SecurityPackage->cbMaxToken; @@ -217,8 +223,12 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, /* Free the decoded challenge as it is not required anymore */ free(chlg); + if(status == SEC_E_INSUFFICIENT_MEMORY) { + return CURLE_OUT_OF_MEMORY; + } + if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { - return CURLE_RECV_ERROR; + return CURLE_AUTH_ERROR; } if(memcmp(&context, krb5->context, sizeof(context))) { @@ -309,7 +319,10 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, if(status != SEC_E_OK) { free(chlg); - return CURLE_OUT_OF_MEMORY; + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; } /* Get the fully qualified username back from the context */ @@ -319,7 +332,10 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, if(status != SEC_E_OK) { free(chlg); - return CURLE_RECV_ERROR; + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; } /* Setup the "input" security buffer */ @@ -380,7 +396,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Convert the user name to UTF8 when operating with Unicode */ - user_name = Curl_convert_tchar_to_UTF8(names.sUserName); + user_name = curlx_convert_tchar_to_UTF8(names.sUserName); if(!user_name) { free(trailer); @@ -392,7 +408,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, message = malloc(messagelen); if(!message) { free(trailer); - Curl_unicodefree(user_name); + curlx_unicodefree(user_name); return CURLE_OUT_OF_MEMORY; } @@ -405,7 +421,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, outdata = htonl(max_size) | sec_layer; memcpy(message, &outdata, sizeof(outdata)); strcpy((char *) message + sizeof(outdata), user_name); - Curl_unicodefree(user_name); + curlx_unicodefree(user_name); /* Allocate the padding */ padding = malloc(sizes.cbBlockSize); @@ -438,7 +454,10 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, free(message); free(trailer); - return CURLE_OUT_OF_MEMORY; + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; } /* Allocate the encryption (wrap) buffer */ diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c index 047c2b5a3fa..3b46e1a4683 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.c +++ b/Utilities/cmcurl/lib/vauth/ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,6 +40,7 @@ #include "curl_ntlm_core.h" #include "curl_gethostname.h" #include "curl_multibyte.h" +#include "curl_md5.h" #include "warnless.h" #include "rand.h" #include "vtls/vtls.h" @@ -599,11 +600,14 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, #endif #if defined(USE_NTRESPONSES) && defined(USE_NTLM2SESSION) + +#define CURL_MD5_DIGEST_LENGTH 16 /* fixed size */ + /* We don't support NTLM2 if we don't have USE_NTRESPONSES */ if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM_KEY) { unsigned char ntbuffer[0x18]; unsigned char tmp[0x18]; - unsigned char md5sum[MD5_DIGEST_LENGTH]; + unsigned char md5sum[CURL_MD5_DIGEST_LENGTH]; unsigned char entropy[8]; /* Need to create 8 bytes random data */ @@ -621,11 +625,11 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, memcpy(tmp, &ntlm->nonce[0], 8); memcpy(tmp + 8, entropy, 8); - result = Curl_ssl_md5sum(tmp, 16, md5sum, MD5_DIGEST_LENGTH); - if(!result) - /* We shall only use the first 8 bytes of md5sum, but the des code in - Curl_ntlm_core_lm_resp only encrypt the first 8 bytes */ - result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer); + Curl_md5it(md5sum, tmp, 16); + + /* We shall only use the first 8 bytes of md5sum, but the des code in + Curl_ntlm_core_lm_resp only encrypt the first 8 bytes */ + result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer); if(result) return result; diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c index 589cca16c08..84ea51dadbe 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c +++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -56,6 +56,11 @@ bool Curl_auth_is_ntlm_supported(void) status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), &SecurityPackage); + /* Release the package buffer as it is not required anymore */ + if(status == SEC_E_OK) { + s_pSecFn->FreeContextBuffer(SecurityPackage); + } + return (status == SEC_E_OK ? TRUE : FALSE); } @@ -100,8 +105,10 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, /* Query the security package for NTLM */ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), &SecurityPackage); - if(status != SEC_E_OK) - return CURLE_NOT_BUILT_IN; + if(status != SEC_E_OK) { + failf(data, "SSPI: couldn't get auth info\n"); + return CURLE_AUTH_ERROR; + } ntlm->token_max = SecurityPackage->cbMaxToken; @@ -169,8 +176,10 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, if(status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc); + else if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) - return CURLE_RECV_ERROR; + return CURLE_AUTH_ERROR; /* Base64 encode the response */ return Curl_base64_encode(data, (char *) ntlm->output_token, @@ -316,7 +325,10 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, infof(data, "NTLM handshake failure (type-3 message): Status=%x\n", status); - return CURLE_RECV_ERROR; + if(status == SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; } /* Base64 encode the response */ diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c index 5d43e110013..ed7ce029ec3 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c @@ -121,7 +121,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, free(spn); - return CURLE_OUT_OF_MEMORY; + return CURLE_AUTH_ERROR; } free(spn); @@ -170,14 +170,14 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, Curl_gss_log_error(data, "gss_init_sec_context() failed: ", major_status, minor_status); - return CURLE_LOGIN_DENIED; + return CURLE_AUTH_ERROR; } if(!output_token.value || !output_token.length) { if(output_token.value) gss_release_buffer(&unused_status, &output_token); - return CURLE_OUT_OF_MEMORY; + return CURLE_AUTH_ERROR; } /* Free previous token */ diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c index 4b21cc769e8..194f250f87f 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -59,6 +59,12 @@ bool Curl_auth_is_spnego_supported(void) TEXT(SP_NAME_NEGOTIATE), &SecurityPackage); + /* Release the package buffer as it is not required anymore */ + if(status == SEC_E_OK) { + s_pSecFn->FreeContextBuffer(SecurityPackage); + } + + return (status == SEC_E_OK ? TRUE : FALSE); } @@ -123,8 +129,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NEGOTIATE), &SecurityPackage); - if(nego->status != SEC_E_OK) - return CURLE_NOT_BUILT_IN; + if(nego->status != SEC_E_OK) { + failf(data, "SSPI: couldn't get auth info\n"); + return CURLE_AUTH_ERROR; + } nego->token_max = SecurityPackage->cbMaxToken; @@ -165,7 +173,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, nego->p_identity, NULL, NULL, nego->credentials, &expiry); if(nego->status != SEC_E_OK) - return CURLE_LOGIN_DENIED; + return CURLE_AUTH_ERROR; /* Allocate our new context handle */ nego->context = calloc(1, sizeof(CtxtHandle)); @@ -251,14 +259,25 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, char buffer[STRERROR_LEN]; failf(data, "InitializeSecurityContext failed: %s", Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; + + if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; } if(nego->status == SEC_I_COMPLETE_NEEDED || nego->status == SEC_I_COMPLETE_AND_CONTINUE) { nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc); if(GSS_ERROR(nego->status)) { - return CURLE_RECV_ERROR; + char buffer[STRERROR_LEN]; + failf(data, "CompleteAuthToken failed: %s", + Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); + + if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) + return CURLE_OUT_OF_MEMORY; + + return CURLE_AUTH_ERROR; } } diff --git a/Utilities/cmcurl/lib/vauth/vauth.c b/Utilities/cmcurl/lib/vauth/vauth.c index a9c5c9c4f72..d98e66c68a8 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.c +++ b/Utilities/cmcurl/lib/vauth/vauth.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2014 - 2019, Steve Holme, . + * Copyright (C) 2014 - 2020, Steve Holme, . * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -89,7 +89,7 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, } /* Allocate our TCHAR based SPN */ - tchar_spn = Curl_convert_UTF8_to_tchar(utf8_spn); + tchar_spn = curlx_convert_UTF8_to_tchar(utf8_spn); if(!tchar_spn) { free(utf8_spn); @@ -97,7 +97,7 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, } /* Release the UTF8 variant when operating with Unicode */ - Curl_unicodefree(utf8_spn); + curlx_unicodefree(utf8_spn); /* Return our newly allocated SPN */ return tchar_spn; diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h index 73bd25ed5ee..a1a557d2a18 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.h +++ b/Utilities/cmcurl/lib/vauth/vauth.h @@ -43,7 +43,7 @@ struct negotiatedata; #endif #if defined(USE_WINDOWS_SSPI) -#define GSS_ERROR(status) (status & 0x80000000) +#define GSS_ERROR(status) ((status) & 0x80000000) #endif /* This is used to build a SPN string */ diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index 14b0531d37c..14e5096065a 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,7 +26,8 @@ #include "urldata.h" #include "vtls/vtls.h" #include "http2.h" -#include "ssh.h" +#include "vssh/ssh.h" +#include "quic.h" #include "curl_printf.h" #ifdef USE_ARES @@ -53,18 +54,6 @@ #include #endif -#ifdef USE_LIBSSH2 -#include -#endif - -#ifdef HAVE_LIBSSH2_VERSION -/* get it run-time if possible */ -#define CURL_LIBSSH2_VERSION libssh2_version(0) -#else -/* use build-time if run-time not possible */ -#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION -#endif - #ifdef HAVE_ZLIB_H #include #ifdef __SYMBIAN32__ @@ -77,16 +66,6 @@ #include #endif -void Curl_version_init(void); - -/* For thread safety purposes this function is called by global_init so that - the static data in both version functions is initialized. */ -void Curl_version_init(void) -{ - curl_version(); - curl_version_info(CURLVERSION_NOW); -} - #ifdef HAVE_BROTLI static size_t brotli_version(char *buf, size_t bufsz) { @@ -99,93 +78,118 @@ static size_t brotli_version(char *buf, size_t bufsz) } #endif +/* + * curl_version() returns a pointer to a static buffer. + * + * It is implemented to work multi-threaded by making sure repeated invokes + * generate the exact same string and never write any temporary data like + * zeros in the data. + */ + +#define VERSION_PARTS 14 /* number of substrings we can concatenate */ + char *curl_version(void) { - static bool initialized; - static char version[200]; - char *ptr = version; - size_t len; - size_t left = sizeof(version); - - if(initialized) - return version; - - strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION); - len = strlen(ptr); - left -= len; - ptr += len; - - if(left > 1) { - len = Curl_ssl_version(ptr + 1, left - 1); - - if(len > 0) { - *ptr = ' '; - left -= ++len; - ptr += len; - } + static char out[300]; + char *outp; + size_t outlen; + const char *src[VERSION_PARTS]; +#ifdef USE_SSL + char ssl_version[200]; +#endif +#ifdef HAVE_LIBZ + char z_version[40]; +#endif +#ifdef HAVE_BROTLI + char br_version[40] = "brotli/"; +#endif +#ifdef USE_ARES + char cares_version[40]; +#endif +#if defined(USE_LIBIDN2) + char idn_version[40]; +#endif +#ifdef USE_LIBPSL + char psl_version[40]; +#endif +#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) + char iconv_version[40]="iconv"; +#endif +#ifdef USE_SSH + char ssh_version[40]; +#endif +#ifdef USE_NGHTTP2 + char h2_version[40]; +#endif +#ifdef ENABLE_QUIC + char h3_version[40]; +#endif +#ifdef USE_LIBRTMP + char rtmp_version[40]; +#endif + int i = 0; + int j; + +#ifdef DEBUGBUILD + /* Override version string when environment variable CURL_VERSION is set */ + const char *debugversion = getenv("CURL_VERSION"); + if(debugversion) { + strncpy(out, debugversion, sizeof(out)-1); + out[sizeof(out)-1] = '\0'; + return out; } +#endif + src[i++] = LIBCURL_NAME "/" LIBCURL_VERSION; +#ifdef USE_SSL + Curl_ssl_version(ssl_version, sizeof(ssl_version)); + src[i++] = ssl_version; +#endif #ifdef HAVE_LIBZ - len = msnprintf(ptr, left, " zlib/%s", zlibVersion()); - left -= len; - ptr += len; + msnprintf(z_version, sizeof(z_version), "zlib/%s", zlibVersion()); + src[i++] = z_version; #endif #ifdef HAVE_BROTLI - len = msnprintf(ptr, left, "%s", " brotli/"); - left -= len; - ptr += len; - len = brotli_version(ptr, left); - left -= len; - ptr += len; + brotli_version(&br_version[7], sizeof(br_version) - 7); + src[i++] = br_version; #endif #ifdef USE_ARES - /* this function is only present in c-ares, not in the original ares */ - len = msnprintf(ptr, left, " c-ares/%s", ares_version(NULL)); - left -= len; - ptr += len; + msnprintf(cares_version, sizeof(cares_version), + "c-ares/%s", ares_version(NULL)); + src[i++] = cares_version; #endif #ifdef USE_LIBIDN2 - if(idn2_check_version(IDN2_VERSION)) { - len = msnprintf(ptr, left, " libidn2/%s", idn2_check_version(NULL)); - left -= len; - ptr += len; - } + msnprintf(idn_version, sizeof(idn_version), + "libidn2/%s", idn2_check_version(NULL)); + src[i++] = idn_version; +#elif defined(USE_WIN32_IDN) + src[i++] = (char *)"WinIDN"; #endif + #ifdef USE_LIBPSL - len = msnprintf(ptr, left, " libpsl/%s", psl_get_version()); - left -= len; - ptr += len; -#endif -#ifdef USE_WIN32_IDN - len = msnprintf(ptr, left, " WinIDN"); - left -= len; - ptr += len; + msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", psl_get_version()); + src[i++] = psl_version; #endif #if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) #ifdef _LIBICONV_VERSION - len = msnprintf(ptr, left, " iconv/%d.%d", - _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255); + msnprintf(iconv_version, sizeof(iconv_version), "iconv/%d.%d", + _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255); #else - /* version unknown */ - len = msnprintf(ptr, left, " iconv"); + /* version unknown, let the default stand */ #endif /* _LIBICONV_VERSION */ - left -= len; - ptr += len; -#endif -#ifdef USE_LIBSSH2 - len = msnprintf(ptr, left, " libssh2/%s", CURL_LIBSSH2_VERSION); - left -= len; - ptr += len; + src[i++] = iconv_version; #endif -#ifdef USE_LIBSSH - len = msnprintf(ptr, left, " libssh/%s", CURL_LIBSSH_VERSION); - left -= len; - ptr += len; +#ifdef USE_SSH + Curl_ssh_version(ssh_version, sizeof(ssh_version)); + src[i++] = ssh_version; #endif #ifdef USE_NGHTTP2 - len = Curl_http2_ver(ptr, left); - left -= len; - ptr += len; + Curl_http2_ver(h2_version, sizeof(h2_version)); + src[i++] = h2_version; +#endif +#ifdef ENABLE_QUIC + Curl_quic_ver(h3_version, sizeof(h3_version)); + src[i++] = h3_version; #endif #ifdef USE_LIBRTMP { @@ -197,27 +201,34 @@ char *curl_version(void) else suff[0] = '\0'; - msnprintf(ptr, left, " librtmp/%d.%d%s", + msnprintf(rtmp_version, sizeof(rtmp_version), "librtmp/%d.%d%s", RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff, suff); -/* - If another lib version is added below this one, this code would - also have to do: - - len = what msnprintf() returned - - left -= len; - ptr += len; -*/ + src[i++] = rtmp_version; } #endif - /* Silent scan-build even if librtmp is not enabled. */ - (void) left; - (void) ptr; + DEBUGASSERT(i <= VERSION_PARTS); + + outp = &out[0]; + outlen = sizeof(out); + for(j = 0; j < i; j++) { + size_t n = strlen(src[j]); + /* we need room for a space, the string and the final zero */ + if(outlen <= (n + 2)) + break; + if(j) { + /* prepend a space if not the first */ + *outp++ = ' '; + outlen--; + } + memcpy(outp, src[j], n); + outp += n; + outlen -= n; + } + *outp = 0; - initialized = true; - return version; + return out; } /* data for curl_version_info @@ -262,6 +273,9 @@ static const char * const protocols[] = { "ldaps", #endif #endif +#ifdef CURL_ENABLE_MQTT + "mqtt", +#endif #ifndef CURL_DISABLE_POP3 "pop3", #endif @@ -274,8 +288,10 @@ static const char * const protocols[] = { #ifndef CURL_DISABLE_RTSP "rtsp", #endif -#if defined(USE_SSH) +#if defined(USE_SSH) && !defined(USE_WOLFSSH) "scp", +#endif +#ifdef USE_SSH "sftp", #endif #if !defined(CURL_DISABLE_SMB) && defined(USE_NTLM) && \ @@ -358,6 +374,9 @@ static curl_version_info_data version_info = { #if defined(USE_NGHTTP2) | CURL_VERSION_HTTP2 #endif +#if defined(ENABLE_QUIC) + | CURL_VERSION_HTTP3 +#endif #if defined(USE_UNIX_SOCKETS) | CURL_VERSION_UNIX_SOCKETS #endif @@ -385,11 +404,23 @@ static curl_version_info_data version_info = { NULL, /* ssh lib version */ 0, /* brotli_ver_num */ NULL, /* brotli version */ + 0, /* nghttp2 version number */ + NULL, /* nghttp2 version string */ + NULL, /* quic library string */ +#ifdef CURL_CA_BUNDLE + CURL_CA_BUNDLE, /* cainfo */ +#else + NULL, +#endif +#ifdef CURL_CA_PATH + CURL_CA_PATH /* capath */ +#else + NULL +#endif }; curl_version_info_data *curl_version_info(CURLversion stamp) { - static bool initialized; #if defined(USE_SSH) static char ssh_buffer[80]; #endif @@ -404,9 +435,6 @@ curl_version_info_data *curl_version_info(CURLversion stamp) static char brotli_buffer[80]; #endif - if(initialized) - return &version_info; - #ifdef USE_SSL Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); version_info.ssl_version = ssl_buffer; @@ -446,11 +474,8 @@ curl_version_info_data *curl_version_info(CURLversion stamp) #endif /* _LIBICONV_VERSION */ #endif -#if defined(USE_LIBSSH2) - msnprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION); - version_info.libssh_version = ssh_buffer; -#elif defined(USE_LIBSSH) - msnprintf(ssh_buffer, sizeof(ssh_buffer), "libssh/%s", CURL_LIBSSH_VERSION); +#if defined(USE_SSH) + Curl_ssh_version(ssh_buffer, sizeof(ssh_buffer)); version_info.libssh_version = ssh_buffer; #endif @@ -460,8 +485,22 @@ curl_version_info_data *curl_version_info(CURLversion stamp) version_info.brotli_version = brotli_buffer; #endif - (void)stamp; /* avoid compiler warnings, we don't use this */ +#ifdef USE_NGHTTP2 + { + nghttp2_info *h2 = nghttp2_version(0); + version_info.nghttp2_ver_num = h2->version_num; + version_info.nghttp2_version = h2->version_str; + } +#endif - initialized = true; +#ifdef ENABLE_QUIC + { + static char quicbuffer[80]; + Curl_quic_ver(quicbuffer, sizeof(quicbuffer)); + version_info.quic_version = quicbuffer; + } +#endif + + (void)stamp; /* avoid compiler warnings, we don't use this */ return &version_info; } diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.c b/Utilities/cmcurl/lib/vquic/ngtcp2.c new file mode 100644 index 00000000000..d29cb378ded --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/ngtcp2.c @@ -0,0 +1,1947 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NGTCP2 +#include +#include +#include +#ifdef USE_OPENSSL +#include +#endif +#include "urldata.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#include "ngtcp2.h" +#include "multiif.h" +#include "strcase.h" +#include "connect.h" +#include "strerror.h" +#include "dynbuf.h" +#include "vquic.h" +#include "vtls/keylog.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* #define DEBUG_NGTCP2 */ +#ifdef CURLDEBUG +#define DEBUG_HTTP3 +#endif +#ifdef DEBUG_HTTP3 +#define H3BUGF(x) x +#else +#define H3BUGF(x) do { } while(0) +#endif + +/* + * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked. + * It is used as a circular buffer. Add new bytes at the end until it reaches + * the far end, then start over at index 0 again. + */ + +#define H3_SEND_SIZE (20*1024) +struct h3out { + uint8_t buf[H3_SEND_SIZE]; + size_t used; /* number of bytes used in the buffer */ + size_t windex; /* index in the buffer where to start writing the next + data block */ +}; + +#define QUIC_MAX_STREAMS (256*1024) +#define QUIC_MAX_DATA (1*1024*1024) +#define QUIC_IDLE_TIMEOUT 60000 /* milliseconds */ + +#ifdef USE_OPENSSL +#define QUIC_CIPHERS \ + "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ + "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" +#define QUIC_GROUPS "P-256:X25519:P-384:P-521" +#elif defined(USE_GNUTLS) +#define QUIC_PRIORITY \ + "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ + "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ + "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1" +#endif + +static CURLcode ng_process_ingress(struct connectdata *conn, + curl_socket_t sockfd, + struct quicsocket *qs); +static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd, + struct quicsocket *qs); +static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, + size_t datalen, void *user_data, + void *stream_user_data); + +static ngtcp2_tstamp timestamp(void) +{ + struct curltime ct = Curl_now(); + return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; +} + +#ifdef DEBUG_NGTCP2 +static void quic_printf(void *user_data, const char *fmt, ...) +{ + va_list ap; + (void)user_data; /* TODO, use this to do infof() instead long-term */ + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} +#endif + +#ifdef USE_OPENSSL +static ngtcp2_crypto_level +quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level) +{ + switch(ossl_level) { + case ssl_encryption_initial: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case ssl_encryption_early_data: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case ssl_encryption_handshake: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case ssl_encryption_application: + return NGTCP2_CRYPTO_LEVEL_APP; + default: + assert(0); + } +} +#elif defined(USE_GNUTLS) +static ngtcp2_crypto_level +quic_from_gtls_level(gnutls_record_encryption_level_t gtls_level) +{ + switch(gtls_level) { + case GNUTLS_ENCRYPTION_LEVEL_INITIAL: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case GNUTLS_ENCRYPTION_LEVEL_EARLY: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case GNUTLS_ENCRYPTION_LEVEL_APPLICATION: + return NGTCP2_CRYPTO_LEVEL_APP; + default: + assert(0); + } +} +#endif + +static void qlog_callback(void *user_data, const void *data, size_t datalen) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + if(qs->qlogfd != -1) { + ssize_t rc = write(qs->qlogfd, data, datalen); + if(rc == -1) { + /* on write error, stop further write attempts */ + close(qs->qlogfd); + qs->qlogfd = -1; + } + } + +} + +static void quic_settings(struct quicsocket *qs, + uint64_t stream_buffer_size) +{ + ngtcp2_settings *s = &qs->settings; + ngtcp2_settings_default(s); +#ifdef DEBUG_NGTCP2 + s->log_printf = quic_printf; +#else + s->log_printf = NULL; +#endif + s->initial_ts = timestamp(); + s->transport_params.initial_max_stream_data_bidi_local = stream_buffer_size; + s->transport_params.initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS; + s->transport_params.initial_max_stream_data_uni = QUIC_MAX_STREAMS; + s->transport_params.initial_max_data = QUIC_MAX_DATA; + s->transport_params.initial_max_streams_bidi = 1; + s->transport_params.initial_max_streams_uni = 3; + s->transport_params.max_idle_timeout = QUIC_IDLE_TIMEOUT; + if(qs->qlogfd != -1) { + s->qlog.write = qlog_callback; + } +} + +#ifdef USE_OPENSSL +static void keylog_callback(const SSL *ssl, const char *line) +{ + (void)ssl; + Curl_tls_keylog_write_line(line); +} +#elif defined(USE_GNUTLS) +static int keylog_callback(gnutls_session_t session, const char *label, + const gnutls_datum_t *secret) +{ + gnutls_datum_t crandom; + gnutls_datum_t srandom; + + gnutls_session_get_random(session, &crandom, &srandom); + if(crandom.size != 32) { + return -1; + } + + Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); + return 0; +} +#endif + +static int init_ngh3_conn(struct quicsocket *qs); + +static int write_client_handshake(struct quicsocket *qs, + ngtcp2_crypto_level level, + const uint8_t *data, size_t len) +{ + struct quic_handshake *crypto_data; + int rv; + + crypto_data = &qs->crypto_data[level]; + if(crypto_data->buf == NULL) { + crypto_data->buf = malloc(4096); + if(!crypto_data->buf) + return 0; + crypto_data->alloclen = 4096; + } + + /* TODO Just pretend that handshake does not grow more than 4KiB for + now */ + assert(crypto_data->len + len <= crypto_data->alloclen); + + memcpy(&crypto_data->buf[crypto_data->len], data, len); + crypto_data->len += len; + + rv = ngtcp2_conn_submit_crypto_data( + qs->qconn, level, (uint8_t *)(&crypto_data->buf[crypto_data->len] - len), + len); + if(rv) { + H3BUGF(fprintf(stderr, "write_client_handshake failed\n")); + } + assert(0 == rv); + + return 1; +} + +#ifdef USE_OPENSSL +static int quic_set_encryption_secrets(SSL *ssl, + OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *rx_secret, + const uint8_t *tx_secret, + size_t secretlen) +{ + struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl); + int level = quic_from_ossl_level(ossl_level); + + if(ngtcp2_crypto_derive_and_install_rx_key( + qs->qconn, NULL, NULL, NULL, level, rx_secret, secretlen) != 0) + return 0; + + if(ngtcp2_crypto_derive_and_install_tx_key( + qs->qconn, NULL, NULL, NULL, level, tx_secret, secretlen) != 0) + return 0; + + if(level == NGTCP2_CRYPTO_LEVEL_APP) { + if(init_ngh3_conn(qs) != CURLE_OK) + return 0; + } + + return 1; +} + +static int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *data, size_t len) +{ + struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl); + ngtcp2_crypto_level level = quic_from_ossl_level(ossl_level); + + return write_client_handshake(qs, level, data, len); +} + +static int quic_flush_flight(SSL *ssl) +{ + (void)ssl; + return 1; +} + +static int quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level, + uint8_t alert) +{ + struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl); + (void)level; + + qs->tls_alert = alert; + return 1; +} + +static SSL_QUIC_METHOD quic_method = {quic_set_encryption_secrets, + quic_add_handshake_data, + quic_flush_flight, quic_send_alert}; + +static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) +{ + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); + + SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); + + SSL_CTX_set_default_verify_paths(ssl_ctx); + + if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) { + char error_buffer[256]; + ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); + failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); + return NULL; + } + + if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) { + failf(data, "SSL_CTX_set1_groups_list failed"); + return NULL; + } + + SSL_CTX_set_quic_method(ssl_ctx, &quic_method); + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); + } + + return ssl_ctx; +} + +/** SSL callbacks ***/ + +static int quic_init_ssl(struct quicsocket *qs) +{ + const uint8_t *alpn = NULL; + size_t alpnlen = 0; + /* this will need some attention when HTTPS proxy over QUIC get fixed */ + const char * const hostname = qs->conn->host.name; + + DEBUGASSERT(!qs->ssl); + qs->ssl = SSL_new(qs->sslctx); + + SSL_set_app_data(qs->ssl, qs); + SSL_set_connect_state(qs->ssl); + + switch(qs->version) { +#ifdef NGTCP2_PROTO_VER + case NGTCP2_PROTO_VER: + alpn = (const uint8_t *)NGHTTP3_ALPN_H3; + alpnlen = sizeof(NGHTTP3_ALPN_H3) - 1; + break; +#endif + } + if(alpn) + SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); + + /* set SNI */ + SSL_set_tlsext_host_name(qs->ssl, hostname); + return 0; +} +#elif defined(USE_GNUTLS) +static int secret_func(gnutls_session_t ssl, + gnutls_record_encryption_level_t gtls_level, + const void *rx_secret, + const void *tx_secret, size_t secretlen) +{ + struct quicsocket *qs = gnutls_session_get_ptr(ssl); + int level = quic_from_gtls_level(gtls_level); + + if(level != NGTCP2_CRYPTO_LEVEL_EARLY && + ngtcp2_crypto_derive_and_install_rx_key( + qs->qconn, NULL, NULL, NULL, level, rx_secret, secretlen) != 0) + return 0; + + if(ngtcp2_crypto_derive_and_install_tx_key( + qs->qconn, NULL, NULL, NULL, level, tx_secret, secretlen) != 0) + return 0; + + if(level == NGTCP2_CRYPTO_LEVEL_APP) { + if(init_ngh3_conn(qs) != CURLE_OK) + return -1; + } + + return 0; +} + +static int read_func(gnutls_session_t ssl, + gnutls_record_encryption_level_t gtls_level, + gnutls_handshake_description_t htype, const void *data, + size_t len) +{ + struct quicsocket *qs = gnutls_session_get_ptr(ssl); + ngtcp2_crypto_level level = quic_from_gtls_level(gtls_level); + int rv; + + if(htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC) + return 0; + + rv = write_client_handshake(qs, level, data, len); + if(rv == 0) + return -1; + + return 0; +} + +static int alert_read_func(gnutls_session_t ssl, + gnutls_record_encryption_level_t gtls_level, + gnutls_alert_level_t alert_level, + gnutls_alert_description_t alert_desc) +{ + struct quicsocket *qs = gnutls_session_get_ptr(ssl); + (void)gtls_level; + (void)alert_level; + + qs->tls_alert = alert_desc; + return 1; +} + +static int tp_recv_func(gnutls_session_t ssl, const uint8_t *data, + size_t data_size) +{ + struct quicsocket *qs = gnutls_session_get_ptr(ssl); + ngtcp2_transport_params params; + + if(ngtcp2_decode_transport_params( + ¶ms, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, + data, data_size) != 0) + return -1; + + if(ngtcp2_conn_set_remote_transport_params(qs->qconn, ¶ms) != 0) + return -1; + + return 0; +} + +static int tp_send_func(gnutls_session_t ssl, gnutls_buffer_t extdata) +{ + struct quicsocket *qs = gnutls_session_get_ptr(ssl); + uint8_t paramsbuf[64]; + ngtcp2_transport_params params; + ssize_t nwrite; + int rc; + + ngtcp2_conn_get_local_transport_params(qs->qconn, ¶ms); + nwrite = ngtcp2_encode_transport_params( + paramsbuf, sizeof(paramsbuf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, + ¶ms); + if(nwrite < 0) { + H3BUGF(fprintf(stderr, "ngtcp2_encode_transport_params: %s\n", + ngtcp2_strerror((int)nwrite))); + return -1; + } + + rc = gnutls_buffer_append_data(extdata, paramsbuf, nwrite); + if(rc < 0) + return rc; + + return (int)nwrite; +} + +static int quic_init_ssl(struct quicsocket *qs) +{ + gnutls_datum_t alpn = {NULL, 0}; + /* this will need some attention when HTTPS proxy over QUIC get fixed */ + const char * const hostname = qs->conn->host.name; + int rc; + + DEBUGASSERT(!qs->ssl); + + gnutls_init(&qs->ssl, GNUTLS_CLIENT); + gnutls_session_set_ptr(qs->ssl, qs); + + rc = gnutls_priority_set_direct(qs->ssl, QUIC_PRIORITY, NULL); + if(rc < 0) { + H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n", + gnutls_strerror(rc))); + return 1; + } + + gnutls_handshake_set_secret_function(qs->ssl, secret_func); + gnutls_handshake_set_read_function(qs->ssl, read_func); + gnutls_alert_set_read_function(qs->ssl, alert_read_func); + + rc = gnutls_session_ext_register(qs->ssl, "QUIC Transport Parameters", + 0xffa5, GNUTLS_EXT_TLS, + tp_recv_func, tp_send_func, + NULL, NULL, NULL, + GNUTLS_EXT_FLAG_TLS | + GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_EE); + if(rc < 0) { + H3BUGF(fprintf(stderr, "gnutls_session_ext_register failed: %s\n", + gnutls_strerror(rc))); + return 1; + } + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + gnutls_session_set_keylog_function(qs->ssl, keylog_callback); + } + + if(qs->cred) + gnutls_certificate_free_credentials(qs->cred); + + rc = gnutls_certificate_allocate_credentials(&qs->cred); + if(rc < 0) { + H3BUGF(fprintf(stderr, + "gnutls_certificate_allocate_credentials failed: %s\n", + gnutls_strerror(rc))); + return 1; + } + + rc = gnutls_certificate_set_x509_system_trust(qs->cred); + if(rc < 0) { + H3BUGF(fprintf(stderr, + "gnutls_certificate_set_x509_system_trust failed: %s\n", + gnutls_strerror(rc))); + return 1; + } + + rc = gnutls_credentials_set(qs->ssl, GNUTLS_CRD_CERTIFICATE, qs->cred); + if(rc < 0) { + H3BUGF(fprintf(stderr, "gnutls_credentials_set failed: %s\n", + gnutls_strerror(rc))); + return 1; + } + + switch(qs->version) { +#ifdef NGTCP2_PROTO_VER + case NGTCP2_PROTO_VER: + /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */ + alpn.data = (unsigned char *)NGHTTP3_ALPN_H3 + 1; + alpn.size = sizeof(NGHTTP3_ALPN_H3) - 2; + break; +#endif + } + if(alpn.data) + gnutls_alpn_set_protocols(qs->ssl, &alpn, 1, 0); + + /* set SNI */ + gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname)); + return 0; +} +#endif + +static int +cb_recv_crypto_data(ngtcp2_conn *tconn, ngtcp2_crypto_level crypto_level, + uint64_t offset, + const uint8_t *data, size_t datalen, + void *user_data) +{ + (void)offset; + (void)user_data; + + if(ngtcp2_crypto_read_write_crypto_data(tconn, crypto_level, data, + datalen) != 0) + return NGTCP2_ERR_CRYPTO; + + return 0; +} + +static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + (void)tconn; + infof(qs->conn->data, "QUIC handshake is completed\n"); + + return 0; +} + +static void extend_stream_window(ngtcp2_conn *tconn, + struct HTTP *stream) +{ + size_t thismuch = stream->unacked_window; + ngtcp2_conn_extend_max_stream_offset(tconn, stream->stream3_id, thismuch); + ngtcp2_conn_extend_max_offset(tconn, thismuch); + stream->unacked_window = 0; +} + + +static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream_id, uint64_t offset, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + ssize_t nconsumed; + int fin = flags & NGTCP2_STREAM_DATA_FLAG_FIN ? 1 : 0; + (void)offset; + (void)stream_user_data; + + nconsumed = + nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin); + if(nconsumed < 0) { + failf(qs->conn->data, "nghttp3_conn_read_stream returned error: %s\n", + nghttp3_strerror((int)nconsumed)); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + /* number of bytes inside buflen which consists of framing overhead + * including QPACK HEADERS. In other words, it does not consume payload of + * DATA frame. */ + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); + ngtcp2_conn_extend_max_offset(tconn, nconsumed); + + return 0; +} + +static int +cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t offset, size_t datalen, void *user_data, + void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + int rv; + (void)stream_id; + (void)tconn; + (void)offset; + (void)datalen; + (void)stream_user_data; + + rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen); + if(rv != 0) { + failf(qs->conn->data, "nghttp3_conn_add_ack_offset returned error: %s\n", + nghttp3_strerror(rv)); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + int rv; + (void)tconn; + (void)stream_user_data; + /* stream is closed... */ + + rv = nghttp3_conn_close_stream(qs->h3conn, stream_id, + app_error_code); + if(rv != 0) { + failf(qs->conn->data, "nghttp3_conn_close_stream returned error: %s\n", + nghttp3_strerror(rv)); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t final_size, uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + int rv; + (void)tconn; + (void)final_size; + (void)app_error_code; + (void)stream_user_data; + + rv = nghttp3_conn_reset_stream(qs->h3conn, stream_id); + if(rv != 0) { + failf(qs->conn->data, "nghttp3_conn_reset_stream returned error: %s\n", + nghttp3_strerror(rv)); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, + uint64_t max_streams, + void *user_data) +{ + (void)tconn; + (void)max_streams; + (void)user_data; + + return 0; +} + +static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t max_data, void *user_data, + void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + int rv; + (void)tconn; + (void)max_data; + (void)stream_user_data; + + rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id); + if(rv != 0) { + failf(qs->conn->data, "nghttp3_conn_unblock_stream returned error: %s\n", + nghttp3_strerror(rv)); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, + uint8_t *token, size_t cidlen, + void *user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + CURLcode result; + (void)tconn; + + result = Curl_rand(qs->conn->data, cid->data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(qs->conn->data, token, NGTCP2_STATELESS_RESET_TOKENLEN); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} + +static ngtcp2_conn_callbacks ng_callbacks = { + ngtcp2_crypto_client_initial_cb, + NULL, /* recv_client_initial */ + cb_recv_crypto_data, + cb_handshake_completed, + NULL, /* recv_version_negotiation */ + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, + cb_recv_stream_data, + NULL, /* acked_crypto_offset */ + cb_acked_stream_data_offset, + NULL, /* stream_open */ + cb_stream_close, + NULL, /* recv_stateless_reset */ + ngtcp2_crypto_recv_retry_cb, + cb_extend_max_local_streams_bidi, + NULL, /* extend_max_local_streams_uni */ + NULL, /* rand */ + cb_get_new_connection_id, + NULL, /* remove_connection_id */ + ngtcp2_crypto_update_key_cb, /* update_key */ + NULL, /* path_validation */ + NULL, /* select_preferred_addr */ + cb_stream_reset, + NULL, /* extend_max_remote_streams_bidi */ + NULL, /* extend_max_remote_streams_uni */ + cb_extend_max_stream_data, + NULL, /* dcid_status */ + NULL, /* handshake_confirmed */ + NULL, /* recv_new_token */ + ngtcp2_crypto_delete_crypto_aead_ctx_cb, + ngtcp2_crypto_delete_crypto_cipher_ctx_cb +}; + +/* + * Might be called twice for happy eyeballs. + */ +CURLcode Curl_quic_connect(struct connectdata *conn, + curl_socket_t sockfd, + int sockindex, + const struct sockaddr *addr, + socklen_t addrlen) +{ + int rc; + int rv; + CURLcode result; + ngtcp2_path path; /* TODO: this must be initialized properly */ + struct Curl_easy *data = conn->data; + struct quicsocket *qs = &conn->hequic[sockindex]; + char ipbuf[40]; + long port; + int qfd; + + if(qs->conn) + Curl_quic_disconnect(conn, sockindex); + qs->conn = conn; + + /* extract the used address as a string */ + if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) { + char buffer[STRERROR_LEN]; + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + infof(data, "Connect socket %d over QUIC to %s:%ld\n", + sockfd, ipbuf, port); + + qs->version = NGTCP2_PROTO_VER; +#ifdef USE_OPENSSL + qs->sslctx = quic_ssl_ctx(data); + if(!qs->sslctx) + return CURLE_QUIC_CONNECT_ERROR; +#endif + + if(quic_init_ssl(qs)) + return CURLE_QUIC_CONNECT_ERROR; + + qs->dcid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + qs->scid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + (void)Curl_qlogdir(data, qs->scid.data, NGTCP2_MAX_CIDLEN, &qfd); + qs->qlogfd = qfd; /* -1 if failure above */ + quic_settings(qs, data->set.buffer_size); + + qs->local_addrlen = sizeof(qs->local_addr); + rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr, + &qs->local_addrlen); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr, qs->local_addrlen, + NULL); + ngtcp2_addr_init(&path.remote, (uint8_t*)addr, addrlen, NULL); + +#ifdef NGTCP2_PROTO_VER +#define QUICVER NGTCP2_PROTO_VER +#else +#error "unsupported ngtcp2 version" +#endif + rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, QUICVER, + &ng_callbacks, &qs->settings, NULL, qs); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl); + + return CURLE_OK; +} + +/* + * Store ngtp2 version info in this buffer, Prefix with a space. Return total + * length written. + */ +int Curl_quic_ver(char *p, size_t len) +{ + ngtcp2_info *ng2 = ngtcp2_version(0); + nghttp3_info *ht3 = nghttp3_version(0); + return msnprintf(p, len, "ngtcp2/%s nghttp3/%s", + ng2->version_str, ht3->version_str); +} + +static int ng_getsock(struct connectdata *conn, curl_socket_t *socks) +{ + struct SingleRequest *k = &conn->data->req; + int bitmap = GETSOCK_BLANK; + + socks[0] = conn->sock[FIRSTSOCKET]; + + /* in a HTTP/2 connection we can basically always get a frame so we should + always be ready for one */ + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + + /* we're still uploading or the HTTP/2 layer wants to send data */ + if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} + +static int ng_perform_getsock(const struct connectdata *conn, + curl_socket_t *socks) +{ + return ng_getsock((struct connectdata *)conn, socks); +} + +static void qs_disconnect(struct quicsocket *qs) +{ + int i; + if(!qs->conn) /* already closed */ + return; + qs->conn = NULL; + if(qs->qlogfd != -1) { + close(qs->qlogfd); + qs->qlogfd = -1; + } + if(qs->ssl) +#ifdef USE_OPENSSL + SSL_free(qs->ssl); +#elif defined(USE_GNUTLS) + gnutls_deinit(qs->ssl); +#endif + qs->ssl = NULL; +#ifdef USE_GNUTLS + if(qs->cred) + gnutls_certificate_free_credentials(qs->cred); +#endif + for(i = 0; i < 3; i++) + Curl_safefree(qs->crypto_data[i].buf); + nghttp3_conn_del(qs->h3conn); + ngtcp2_conn_del(qs->qconn); +#ifdef USE_OPENSSL + SSL_CTX_free(qs->sslctx); +#endif +} + +void Curl_quic_disconnect(struct connectdata *conn, + int tempindex) +{ + if(conn->transport == TRNSPRT_QUIC) + qs_disconnect(&conn->hequic[tempindex]); +} + +static CURLcode ng_disconnect(struct connectdata *conn, + bool dead_connection) +{ + (void)dead_connection; + Curl_quic_disconnect(conn, 0); + Curl_quic_disconnect(conn, 1); + return CURLE_OK; +} + +static unsigned int ng_conncheck(struct connectdata *conn, + unsigned int checks_to_perform) +{ + (void)conn; + (void)checks_to_perform; + return CONNRESULT_NONE; +} + +static const struct Curl_handler Curl_handler_http3 = { + "HTTPS", /* scheme */ + ZERO_NULL, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ng_getsock, /* proto_getsock */ + ng_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ng_perform_getsock, /* perform_getsock */ + ng_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + ng_conncheck, /* connection_check */ + PORT_HTTP, /* defport */ + CURLPROTO_HTTPS, /* protocol */ + PROTOPT_SSL | PROTOPT_STREAM /* flags */ +}; + +static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.protop; + (void)conn; + (void)stream_id; + (void)app_error_code; + (void)user_data; + H3BUGF(infof(data, "cb_h3_stream_close CALLED\n")); + + stream->closed = TRUE; + Curl_expire(data, 0, EXPIRE_QUIC); + /* make sure that ngh3_stream_recv is called again to complete the transfer + even if there are no more packets to be received from the server. */ + data->state.drain = 1; + return 0; +} + +/* + * write_data() copies data to the stream's receive buffer. If not enough + * space is available in the receive buffer, it copies the rest to the + * stream's overflow buffer. + */ +static CURLcode write_data(struct HTTP *stream, const void *mem, size_t memlen) +{ + CURLcode result = CURLE_OK; + const char *buf = mem; + size_t ncopy = memlen; + /* copy as much as possible to the receive buffer */ + if(stream->len) { + size_t len = CURLMIN(ncopy, stream->len); + memcpy(stream->mem, buf, len); + stream->len -= len; + stream->memlen += len; + stream->mem += len; + buf += len; + ncopy -= len; + } + /* copy the rest to the overflow buffer */ + if(ncopy) + result = Curl_dyn_addn(&stream->overflow, buf, ncopy); + return result; +} + +static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.protop; + CURLcode result = CURLE_OK; + (void)conn; + + result = write_data(stream, buf, buflen); + if(result) { + return -1; + } + stream->unacked_window += buflen; + (void)stream_id; + (void)user_data; + return 0; +} + +static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, + size_t consumed, void *user_data, + void *stream_user_data) +{ + struct quicsocket *qs = user_data; + (void)conn; + (void)stream_user_data; + (void)stream_id; + + ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed); + ngtcp2_conn_extend_max_offset(qs->qconn, consumed); + return 0; +} + +/* Decode HTTP status code. Returns -1 if no valid status code was + decoded. (duplicate from http2.c) */ +static int decode_status_code(const uint8_t *value, size_t len) +{ + int i; + int res; + + if(len != 3) { + return -1; + } + + res = 0; + + for(i = 0; i < 3; ++i) { + char c = value[i]; + + if(c < '0' || c > '9') { + return -1; + } + + res *= 10; + res += c - '0'; + } + + return res; +} + +static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, + void *user_data, void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.protop; + CURLcode result = CURLE_OK; + (void)conn; + (void)stream_id; + (void)user_data; + + /* add a CRLF only if we've received some headers */ + if(stream->firstheader) { + result = write_data(stream, "\r\n", 2); + if(result) { + return -1; + } + } + return 0; +} + +static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *user_data, void *stream_user_data) +{ + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.protop; + CURLcode result = CURLE_OK; + (void)conn; + (void)stream_id; + (void)token; + (void)flags; + (void)user_data; + + if(h3name.len == sizeof(":status") - 1 && + !memcmp(":status", h3name.base, h3name.len)) { + char line[14]; /* status line is always 13 characters long */ + size_t ncopy; + int status = decode_status_code(h3val.base, h3val.len); + DEBUGASSERT(status != -1); + ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", status); + result = write_data(stream, line, ncopy); + if(result) { + return -1; + } + } + else { + /* store as a HTTP1-style header */ + result = write_data(stream, h3name.base, h3name.len); + if(result) { + return -1; + } + result = write_data(stream, ": ", 2); + if(result) { + return -1; + } + result = write_data(stream, h3val.base, h3val.len); + if(result) { + return -1; + } + result = write_data(stream, "\r\n", 2); + if(result) { + return -1; + } + } + + stream->firstheader = TRUE; + return 0; +} + +static int cb_h3_send_stop_sending(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *user_data, + void *stream_user_data) +{ + (void)conn; + (void)stream_id; + (void)app_error_code; + (void)user_data; + (void)stream_user_data; + return 0; +} + +static nghttp3_conn_callbacks ngh3_callbacks = { + cb_h3_acked_stream_data, /* acked_stream_data */ + cb_h3_stream_close, + cb_h3_recv_data, + cb_h3_deferred_consume, + NULL, /* begin_headers */ + cb_h3_recv_header, + cb_h3_end_headers, + NULL, /* begin_trailers */ + cb_h3_recv_header, + NULL, /* end_trailers */ + NULL, /* http_begin_push_promise */ + NULL, /* http_recv_push_promise */ + NULL, /* http_end_push_promise */ + NULL, /* http_cancel_push */ + cb_h3_send_stop_sending, + NULL, /* push_stream */ + NULL, /* end_stream */ +}; + +static int init_ngh3_conn(struct quicsocket *qs) +{ + CURLcode result; + int rc; + int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; + + if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) { + failf(qs->conn->data, "too few available QUIC streams"); + return CURLE_QUIC_CONNECT_ERROR; + } + + nghttp3_conn_settings_default(&qs->h3settings); + + rc = nghttp3_conn_client_new(&qs->h3conn, + &ngh3_callbacks, + &qs->h3settings, + nghttp3_mem_default(), + qs); + if(rc) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id, + qpack_dec_stream_id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + return CURLE_OK; + fail: + + return result; +} + +static Curl_recv ngh3_stream_recv; +static Curl_send ngh3_stream_send; + +static size_t drain_overflow_buffer(struct HTTP *stream) +{ + size_t overlen = Curl_dyn_len(&stream->overflow); + size_t ncopy = CURLMIN(overlen, stream->len); + if(ncopy > 0) { + memcpy(stream->mem, Curl_dyn_ptr(&stream->overflow), ncopy); + stream->len -= ncopy; + stream->mem += ncopy; + stream->memlen += ncopy; + if(ncopy != overlen) + /* make the buffer only keep the tail */ + (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy); + } + return ncopy; +} + +/* incoming data frames on the h3 stream */ +static ssize_t ngh3_stream_recv(struct connectdata *conn, + int sockindex, + char *buf, + size_t buffersize, + CURLcode *curlcode) +{ + curl_socket_t sockfd = conn->sock[sockindex]; + struct HTTP *stream = conn->data->req.protop; + struct quicsocket *qs = conn->quic; + + if(!stream->memlen) { + /* remember where to store incoming data for this stream and how big the + buffer is */ + stream->mem = buf; + stream->len = buffersize; + } + /* else, there's data in the buffer already */ + + /* if there's data in the overflow buffer from a previous call, copy as much + as possible to the receive buffer before receiving more */ + drain_overflow_buffer(stream); + + if(ng_process_ingress(conn, sockfd, qs)) { + *curlcode = CURLE_RECV_ERROR; + return -1; + } + if(ng_flush_egress(conn, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + + if(stream->memlen) { + ssize_t memlen = stream->memlen; + /* data arrived */ + *curlcode = CURLE_OK; + /* reset to allow more data to come */ + stream->memlen = 0; + stream->mem = buf; + stream->len = buffersize; + /* extend the stream window with the data we're consuming and send out + any additional packets to tell the server that we can receive more */ + extend_stream_window(qs->qconn, stream); + if(ng_flush_egress(conn, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + return memlen; + } + + if(stream->closed) { + *curlcode = CURLE_OK; + return 0; + } + + infof(conn->data, "ngh3_stream_recv returns 0 bytes and EAGAIN\n"); + *curlcode = CURLE_AGAIN; + return -1; +} + +/* this amount of data has now been acked on this stream */ +static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, + size_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.protop; + (void)conn; + (void)stream_id; + (void)user_data; + + if(!data->set.postfields) { + stream->h3out->used -= datalen; + H3BUGF(infof(data, + "cb_h3_acked_stream_data, %zd bytes, %zd left unacked\n", + datalen, stream->h3out->used)); + DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE); + } + return 0; +} + +static ssize_t cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id, + nghttp3_vec *vec, size_t veccnt, + uint32_t *pflags, void *user_data, + void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + size_t nread; + struct HTTP *stream = data->req.protop; + (void)conn; + (void)stream_id; + (void)user_data; + (void)veccnt; + + if(data->set.postfields) { + vec[0].base = data->set.postfields; + vec[0].len = data->state.infilesize; + *pflags = NGHTTP3_DATA_FLAG_EOF; + return 1; + } + + nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used); + if(nread > 0) { + /* nghttp3 wants us to hold on to the data until it tells us it is okay to + delete it. Append the data at the end of the h3out buffer. Since we can + only return consecutive data, copy the amount that fits and the next + part comes in next invoke. */ + struct h3out *out = stream->h3out; + if(nread + out->windex > H3_SEND_SIZE) + nread = H3_SEND_SIZE - out->windex; + + memcpy(&out->buf[out->windex], stream->upload_mem, nread); + out->windex += nread; + out->used += nread; + + /* that's the chunk we return to nghttp3 */ + vec[0].base = &out->buf[out->windex]; + vec[0].len = nread; + + if(out->windex == H3_SEND_SIZE) + out->windex = 0; /* wrap */ + stream->upload_mem += nread; + stream->upload_len -= nread; + if(data->state.infilesize != -1) { + stream->upload_left -= nread; + if(!stream->upload_left) + *pflags = NGHTTP3_DATA_FLAG_EOF; + } + H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)\n", + nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", + out->used)); + } + if(stream->upload_done && !stream->upload_len && + (stream->upload_left <= 0)) { + H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF\n")); + *pflags = NGHTTP3_DATA_FLAG_EOF; + return 0; + } + else if(!nread) { + return NGHTTP3_ERR_WOULDBLOCK; + } + return 1; +} + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +static CURLcode http_request(struct connectdata *conn, const void *mem, + size_t len) +{ + struct HTTP *stream = conn->data->req.protop; + size_t nheader; + size_t i; + size_t authority_idx; + char *hdbuf = (char *)mem; + char *end, *line_end; + struct quicsocket *qs = conn->quic; + CURLcode result = CURLE_OK; + struct Curl_easy *data = conn->data; + nghttp3_nv *nva = NULL; + int64_t stream3_id; + int rc; + struct h3out *h3out = NULL; + + rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL); + if(rc) { + failf(conn->data, "can get bidi streams"); + result = CURLE_SEND_ERROR; + goto fail; + } + + stream->stream3_id = stream3_id; + stream->h3req = TRUE; /* senf off! */ + Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE); + + /* Calculate number of headers contained in [mem, mem + len). Assumes a + correctly generated HTTP header field block. */ + nheader = 0; + for(i = 1; i < len; ++i) { + if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { + ++nheader; + ++i; + } + } + if(nheader < 2) + goto fail; + + /* We counted additional 2 \r\n in the first and last line. We need 3 + new headers: :method, :path and :scheme. Therefore we need one + more space. */ + nheader += 1; + nva = malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + /* Extract :method, :path from request line + We do line endings with CRLF so checking for CR is enough */ + line_end = memchr(hdbuf, '\r', len); + if(!line_end) { + result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */ + goto fail; + } + + /* Method does not contain spaces */ + end = memchr(hdbuf, ' ', line_end - hdbuf); + if(!end || end == hdbuf) + goto fail; + nva[0].name = (unsigned char *)":method"; + nva[0].namelen = strlen((char *)nva[0].name); + nva[0].value = (unsigned char *)hdbuf; + nva[0].valuelen = (size_t)(end - hdbuf); + nva[0].flags = NGHTTP3_NV_FLAG_NONE; + + hdbuf = end + 1; + + /* Path may contain spaces so scan backwards */ + end = NULL; + for(i = (size_t)(line_end - hdbuf); i; --i) { + if(hdbuf[i - 1] == ' ') { + end = &hdbuf[i - 1]; + break; + } + } + if(!end || end == hdbuf) + goto fail; + nva[1].name = (unsigned char *)":path"; + nva[1].namelen = strlen((char *)nva[1].name); + nva[1].value = (unsigned char *)hdbuf; + nva[1].valuelen = (size_t)(end - hdbuf); + nva[1].flags = NGHTTP3_NV_FLAG_NONE; + + nva[2].name = (unsigned char *)":scheme"; + nva[2].namelen = strlen((char *)nva[2].name); + if(conn->handler->flags & PROTOPT_SSL) + nva[2].value = (unsigned char *)"https"; + else + nva[2].value = (unsigned char *)"http"; + nva[2].valuelen = strlen((char *)nva[2].value); + nva[2].flags = NGHTTP3_NV_FLAG_NONE; + + + authority_idx = 0; + i = 3; + while(i < nheader) { + size_t hlen; + + hdbuf = line_end + 2; + + /* check for next CR, but only within the piece of data left in the given + buffer */ + line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); + if(!line_end || (line_end == hdbuf)) + goto fail; + + /* header continuation lines are not supported */ + if(*hdbuf == ' ' || *hdbuf == '\t') + goto fail; + + for(end = hdbuf; end < line_end && *end != ':'; ++end) + ; + if(end == hdbuf || end == line_end) + goto fail; + hlen = end - hdbuf; + + if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { + authority_idx = i; + nva[i].name = (unsigned char *)":authority"; + nva[i].namelen = strlen((char *)nva[i].name); + } + else { + nva[i].namelen = (size_t)(end - hdbuf); + /* Lower case the header name for HTTP/3 */ + Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen); + nva[i].name = (unsigned char *)hdbuf; + } + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + hdbuf = end + 1; + while(*hdbuf == ' ' || *hdbuf == '\t') + ++hdbuf; + end = line_end; + +#if 0 /* This should probably go in more or less like this */ + switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, + end - hdbuf)) { + case HEADERINST_IGNORE: + /* skip header fields prohibited by HTTP/2 specification. */ + --nheader; + continue; + case HEADERINST_TE_TRAILERS: + nva[i].value = (uint8_t*)"trailers"; + nva[i].value_len = sizeof("trailers") - 1; + break; + default: + nva[i].value = (unsigned char *)hdbuf; + nva[i].value_len = (size_t)(end - hdbuf); + } +#endif + nva[i].value = (unsigned char *)hdbuf; + nva[i].valuelen = (size_t)(end - hdbuf); + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + + ++i; + } + + /* :authority must come before non-pseudo header fields */ + if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) { + nghttp3_nv authority = nva[authority_idx]; + for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { + nva[i] = nva[i - 1]; + } + nva[i] = authority; + } + + /* Warn stream may be rejected if cumulative length of headers is too + large. */ +#define MAX_ACC 60000 /* <64KB to account for some overhead */ + { + size_t acc = 0; + for(i = 0; i < nheader; ++i) + acc += nva[i].namelen + nva[i].valuelen; + + if(acc > MAX_ACC) { + infof(data, "http_request: Warning: The cumulative length of all " + "headers exceeds %zu bytes and that could cause the " + "stream to be rejected.\n", MAX_ACC); + } + } + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: { + nghttp3_data_reader data_reader; + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown, but not zero */ + + data_reader.read_data = cb_h3_readfunction; + + h3out = calloc(sizeof(struct h3out), 1); + if(!h3out) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + stream->h3out = h3out; + + rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, + nva, nheader, &data_reader, + conn->data); + if(rc) { + result = CURLE_SEND_ERROR; + goto fail; + } + break; + } + default: + stream->upload_left = 0; /* nothing left to send */ + rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, + nva, nheader, + NULL, /* no body! */ + conn->data); + if(rc) { + result = CURLE_SEND_ERROR; + goto fail; + } + break; + } + + Curl_safefree(nva); + + infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n", + stream3_id, (void *)data); + + return CURLE_OK; + +fail: + free(nva); + return result; +} +static ssize_t ngh3_stream_send(struct connectdata *conn, + int sockindex, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + ssize_t sent; + struct quicsocket *qs = conn->quic; + curl_socket_t sockfd = conn->sock[sockindex]; + struct HTTP *stream = conn->data->req.protop; + + if(!stream->h3req) { + CURLcode result = http_request(conn, mem, len); + if(result) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + sent = len; + } + else { + H3BUGF(infof(conn->data, "ngh3_stream_send() wants to send %zd bytes\n", + len)); + if(!stream->upload_len) { + stream->upload_mem = mem; + stream->upload_len = len; + (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); + sent = len; + } + else { + *curlcode = CURLE_AGAIN; + return -1; + } + } + + if(ng_flush_egress(conn, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + + *curlcode = CURLE_OK; + return sent; +} + +static void ng_has_connected(struct connectdata *conn, int tempindex) +{ + conn->recv[FIRSTSOCKET] = ngh3_stream_recv; + conn->send[FIRSTSOCKET] = ngh3_stream_send; + conn->handler = &Curl_handler_http3; + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->httpversion = 30; + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + conn->quic = &conn->hequic[tempindex]; + DEBUGF(infof(conn->data, "ngtcp2 established connection!\n")); +} + +/* + * There can be multiple connection attempts going on in parallel. + */ +CURLcode Curl_quic_is_connected(struct connectdata *conn, + int sockindex, + bool *done) +{ + CURLcode result; + struct quicsocket *qs = &conn->hequic[sockindex]; + curl_socket_t sockfd = conn->tempsock[sockindex]; + + result = ng_process_ingress(conn, sockfd, qs); + if(result) + goto error; + + result = ng_flush_egress(conn, sockfd, qs); + if(result) + goto error; + + if(ngtcp2_conn_get_handshake_completed(qs->qconn)) { + *done = TRUE; + ng_has_connected(conn, sockindex); + } + + return result; + error: + (void)qs_disconnect(qs); + return result; + +} + +static CURLcode ng_process_ingress(struct connectdata *conn, int sockfd, + struct quicsocket *qs) +{ + ssize_t recvd; + int rv; + uint8_t buf[65536]; + size_t bufsize = sizeof(buf); + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen; + ngtcp2_path path; + ngtcp2_tstamp ts = timestamp(); + + for(;;) { + remote_addrlen = sizeof(remote_addr); + while((recvd = recvfrom(sockfd, buf, bufsize, 0, + (struct sockaddr *)&remote_addr, + &remote_addrlen)) == -1 && + SOCKERRNO == EINTR) + ; + if(recvd == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) + break; + + failf(conn->data, "ngtcp2: recvfrom() unexpectedly returned %d", recvd); + return CURLE_RECV_ERROR; + } + + ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr, + qs->local_addrlen, NULL); + ngtcp2_addr_init(&path.remote, (uint8_t *)&remote_addr, remote_addrlen, + NULL); + + rv = ngtcp2_conn_read_pkt(qs->qconn, &path, buf, recvd, ts); + if(rv != 0) { + /* TODO Send CONNECTION_CLOSE if possible */ + return CURLE_RECV_ERROR; + } + } + + return CURLE_OK; +} + +static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd, + struct quicsocket *qs) +{ + int rv; + ssize_t sent; + ssize_t outlen; + uint8_t out[NGTCP2_MAX_PKTLEN_IPV4]; + size_t pktlen; + ngtcp2_path_storage ps; + ngtcp2_tstamp ts = timestamp(); + struct sockaddr_storage remote_addr; + ngtcp2_tstamp expiry; + ngtcp2_duration timeout; + int64_t stream_id; + ssize_t veccnt; + int fin; + nghttp3_vec vec[16]; + ssize_t ndatalen; + + switch(qs->local_addr.ss_family) { + case AF_INET: + pktlen = NGTCP2_MAX_PKTLEN_IPV4; + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + pktlen = NGTCP2_MAX_PKTLEN_IPV6; + break; +#endif + default: + assert(0); + } + + rv = ngtcp2_conn_handle_expiry(qs->qconn, ts); + if(rv != 0) { + failf(conn->data, "ngtcp2_conn_handle_expiry returned error: %s\n", + ngtcp2_strerror(rv)); + return CURLE_SEND_ERROR; + } + + ngtcp2_path_storage_zero(&ps); + + for(;;) { + outlen = -1; + if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) { + veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec, + sizeof(vec) / sizeof(vec[0])); + if(veccnt < 0) { + failf(conn->data, "nghttp3_conn_writev_stream returned error: %s\n", + nghttp3_strerror((int)veccnt)); + return CURLE_SEND_ERROR; + } + else if(veccnt > 0) { + uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_MORE | + (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); + outlen = + ngtcp2_conn_writev_stream(qs->qconn, &ps.path, + out, pktlen, &ndatalen, + flags, stream_id, + (const ngtcp2_vec *)vec, veccnt, ts); + if(outlen == 0) { + break; + } + if(outlen < 0) { + if(outlen == NGTCP2_ERR_STREAM_DATA_BLOCKED || + outlen == NGTCP2_ERR_STREAM_SHUT_WR) { + assert(ndatalen == -1); + rv = nghttp3_conn_block_stream(qs->h3conn, stream_id); + if(rv != 0) { + failf(conn->data, + "nghttp3_conn_block_stream returned error: %s\n", + nghttp3_strerror(rv)); + return CURLE_SEND_ERROR; + } + continue; + } + else if(outlen == NGTCP2_ERR_WRITE_STREAM_MORE) { + assert(ndatalen > 0); + rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, + ndatalen); + if(rv != 0) { + failf(conn->data, + "nghttp3_conn_add_write_offset returned error: %s\n", + nghttp3_strerror(rv)); + return CURLE_SEND_ERROR; + } + continue; + } + else { + assert(ndatalen == -1); + failf(conn->data, "ngtcp2_conn_writev_stream returned error: %s\n", + ngtcp2_strerror((int)outlen)); + return CURLE_SEND_ERROR; + } + } + else { + assert(ndatalen == -1); + } + } + } + if(outlen < 0) { + outlen = ngtcp2_conn_write_pkt(qs->qconn, &ps.path, out, pktlen, ts); + if(outlen < 0) { + failf(conn->data, "ngtcp2_conn_write_pkt returned error: %s\n", + ngtcp2_strerror((int)outlen)); + return CURLE_SEND_ERROR; + } + if(outlen == 0) + break; + } + + memcpy(&remote_addr, ps.path.remote.addr, ps.path.remote.addrlen); + while((sent = send(sockfd, out, outlen, 0)) == -1 && + SOCKERRNO == EINTR) + ; + + if(sent == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + /* TODO Cache packet */ + break; + } + else { + failf(conn->data, "send() returned %zd (errno %d)\n", sent, + SOCKERRNO); + return CURLE_SEND_ERROR; + } + } + } + + expiry = ngtcp2_conn_get_expiry(qs->qconn); + if(expiry != UINT64_MAX) { + if(expiry <= ts) { + timeout = NGTCP2_MILLISECONDS; + } + else { + timeout = expiry - ts; + } + Curl_expire(conn->data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); + } + + return CURLE_OK; +} + +/* + * Called from transfer.c:done_sending when we stop HTTP/3 uploading. + */ +CURLcode Curl_quic_done_sending(struct connectdata *conn) +{ + if(conn->handler == &Curl_handler_http3) { + /* only for HTTP/3 transfers */ + struct HTTP *stream = conn->data->req.protop; + struct quicsocket *qs = conn->quic; + stream->upload_done = TRUE; + (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); + } + + return CURLE_OK; +} + +/* + * Called from http.c:Curl_http_done when a request completes. + */ +void Curl_quic_done(struct Curl_easy *data, bool premature) +{ + (void)premature; + if(data->conn->handler == &Curl_handler_http3) { + /* only for HTTP/3 transfers */ + struct HTTP *stream = data->req.protop; + Curl_dyn_free(&stream->overflow); + } +} + +/* + * Called from transfer.c:data_pending to know if we should keep looping + * to receive more data from the connection. + */ +bool Curl_quic_data_pending(const struct Curl_easy *data) +{ + /* We may have received more data than we're able to hold in the receive + buffer and allocated an overflow buffer. Since it's possible that + there's no more data coming on the socket, we need to keep reading + until the overflow buffer is empty. */ + const struct HTTP *stream = data->req.protop; + return Curl_dyn_len(&stream->overflow) > 0; +} + +#endif diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.h b/Utilities/cmcurl/lib/vquic/ngtcp2.h new file mode 100644 index 00000000000..e2f8b560016 --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/ngtcp2.h @@ -0,0 +1,73 @@ +#ifndef HEADER_CURL_VQUIC_NGTCP2_H +#define HEADER_CURL_VQUIC_NGTCP2_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NGTCP2 + +#include +#include +#ifdef USE_OPENSSL +#include +#elif defined(USE_GNUTLS) +#include +#endif + +struct quic_handshake { + char *buf; /* pointer to the buffer */ + size_t alloclen; /* size of allocation */ + size_t len; /* size of content in buffer */ + size_t nread; /* how many bytes have been read */ +}; + +struct quicsocket { + struct connectdata *conn; /* point back to the connection */ + ngtcp2_conn *qconn; + ngtcp2_cid dcid; + ngtcp2_cid scid; + uint32_t version; + ngtcp2_settings settings; +#ifdef USE_OPENSSL + SSL_CTX *sslctx; + SSL *ssl; +#elif defined(USE_GNUTLS) + gnutls_certificate_credentials_t cred; + gnutls_session_t ssl; +#endif + struct quic_handshake crypto_data[3]; + /* the last TLS alert description generated by the local endpoint */ + uint8_t tls_alert; + struct sockaddr_storage local_addr; + socklen_t local_addrlen; + + nghttp3_conn *h3conn; + nghttp3_conn_settings h3settings; + int qlogfd; +}; + +#include "urldata.h" + +#endif + +#endif /* HEADER_CURL_VQUIC_NGTCP2_H */ diff --git a/Utilities/cmcurl/lib/vquic/quiche.c b/Utilities/cmcurl/lib/vquic/quiche.c new file mode 100644 index 00000000000..be6f15c1999 --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/quiche.c @@ -0,0 +1,853 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_QUICHE +#include +#include +#include "urldata.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#include "quic.h" +#include "strcase.h" +#include "multiif.h" +#include "connect.h" +#include "strerror.h" +#include "vquic.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define DEBUG_HTTP3 +/* #define DEBUG_QUICHE */ +#ifdef DEBUG_HTTP3 +#define H3BUGF(x) x +#else +#define H3BUGF(x) do { } while(0) +#endif + +#define QUIC_MAX_STREAMS (256*1024) +#define QUIC_MAX_DATA (1*1024*1024) +#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */ + +static CURLcode process_ingress(struct connectdata *conn, + curl_socket_t sockfd, + struct quicsocket *qs); + +static CURLcode flush_egress(struct connectdata *conn, curl_socket_t sockfd, + struct quicsocket *qs); + +static CURLcode http_request(struct connectdata *conn, const void *mem, + size_t len); +static Curl_recv h3_stream_recv; +static Curl_send h3_stream_send; + +static int quiche_getsock(struct connectdata *conn, curl_socket_t *socks) +{ + struct SingleRequest *k = &conn->data->req; + int bitmap = GETSOCK_BLANK; + + socks[0] = conn->sock[FIRSTSOCKET]; + + /* in a HTTP/2 connection we can basically always get a frame so we should + always be ready for one */ + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + + /* we're still uploading or the HTTP/2 layer wants to send data */ + if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} + +static int quiche_perform_getsock(const struct connectdata *conn, + curl_socket_t *socks) +{ + return quiche_getsock((struct connectdata *)conn, socks); +} + +static CURLcode qs_disconnect(struct quicsocket *qs) +{ + if(qs->h3config) + quiche_h3_config_free(qs->h3config); + if(qs->h3c) + quiche_h3_conn_free(qs->h3c); + quiche_config_free(qs->cfg); + quiche_conn_free(qs->conn); + return CURLE_OK; +} + +static CURLcode quiche_disconnect(struct connectdata *conn, + bool dead_connection) +{ + struct quicsocket *qs = conn->quic; + (void)dead_connection; + return qs_disconnect(qs); +} + +void Curl_quic_disconnect(struct connectdata *conn, + int tempindex) +{ + if(conn->transport == TRNSPRT_QUIC) + qs_disconnect(&conn->hequic[tempindex]); +} + +static unsigned int quiche_conncheck(struct connectdata *conn, + unsigned int checks_to_perform) +{ + (void)conn; + (void)checks_to_perform; + return CONNRESULT_NONE; +} + +static CURLcode quiche_do(struct connectdata *conn, bool *done) +{ + struct HTTP *stream = conn->data->req.protop; + stream->h3req = FALSE; /* not sent */ + return Curl_http(conn, done); +} + +static const struct Curl_handler Curl_handler_http3 = { + "HTTPS", /* scheme */ + ZERO_NULL, /* setup_connection */ + quiche_do, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + quiche_getsock, /* proto_getsock */ + quiche_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + quiche_perform_getsock, /* perform_getsock */ + quiche_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + quiche_conncheck, /* connection_check */ + PORT_HTTP, /* defport */ + CURLPROTO_HTTPS, /* protocol */ + PROTOPT_SSL | PROTOPT_STREAM /* flags */ +}; + +#ifdef DEBUG_QUICHE +static void quiche_debug_log(const char *line, void *argp) +{ + (void)argp; + fprintf(stderr, "%s\n", line); +} +#endif + +CURLcode Curl_quic_connect(struct connectdata *conn, curl_socket_t sockfd, + int sockindex, + const struct sockaddr *addr, socklen_t addrlen) +{ + CURLcode result; + struct quicsocket *qs = &conn->hequic[sockindex]; + struct Curl_easy *data = conn->data; + char *keylog_file = NULL; + +#ifdef DEBUG_QUICHE + /* initialize debug log callback only once */ + static int debug_log_init = 0; + if(!debug_log_init) { + quiche_enable_debug_logging(quiche_debug_log, NULL); + debug_log_init = 1; + } +#endif + + (void)addr; + (void)addrlen; + + qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); + if(!qs->cfg) { + failf(data, "can't create quiche config"); + return CURLE_FAILED_INIT; + } + + quiche_config_set_max_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT); + quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg, + QUIC_MAX_DATA); + quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS); + quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS); + quiche_config_set_application_protos(qs->cfg, + (uint8_t *) + QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) + - 1); + + result = Curl_rand(data, qs->scid, sizeof(qs->scid)); + if(result) + return result; + + keylog_file = getenv("SSLKEYLOGFILE"); + + if(keylog_file) + quiche_config_log_keys(qs->cfg); + + qs->conn = quiche_connect(conn->host.name, (const uint8_t *) qs->scid, + sizeof(qs->scid), qs->cfg); + if(!qs->conn) { + failf(data, "can't create quiche connection"); + return CURLE_OUT_OF_MEMORY; + } + + if(keylog_file) + quiche_conn_set_keylog_path(qs->conn, keylog_file); + + /* Known to not work on Windows */ +#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) + { + int qfd; + (void)Curl_qlogdir(data, qs->scid, sizeof(qs->scid), &qfd); + if(qfd != -1) + quiche_conn_set_qlog_fd(qs->conn, qfd, + "qlog title", "curl qlog"); + } +#endif + + result = flush_egress(conn, sockfd, qs); + if(result) + return result; + + /* store the used address as a string */ + if(!Curl_addr2string((struct sockaddr*)addr, addrlen, + conn->primary_ip, &conn->primary_port)) { + char buffer[STRERROR_LEN]; + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN); + Curl_persistconninfo(conn); + + /* for connection reuse purposes: */ + conn->ssl[FIRSTSOCKET].state = ssl_connection_complete; + + { + unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL; + unsigned alpn_len, offset = 0; + + /* Replace each ALPN length prefix by a comma. */ + while(offset < sizeof(alpn_protocols) - 1) { + alpn_len = alpn_protocols[offset]; + alpn_protocols[offset] = ','; + offset += 1 + alpn_len; + } + + infof(data, "Sent QUIC client Initial, ALPN: %s\n", + alpn_protocols + 1); + } + + return CURLE_OK; +} + +static CURLcode quiche_has_connected(struct connectdata *conn, + int sockindex, + int tempindex) +{ + CURLcode result; + struct quicsocket *qs = conn->quic = &conn->hequic[tempindex]; + + conn->recv[sockindex] = h3_stream_recv; + conn->send[sockindex] = h3_stream_send; + conn->handler = &Curl_handler_http3; + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->httpversion = 30; + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + + qs->h3config = quiche_h3_config_new(); + if(!qs->h3config) + return CURLE_OUT_OF_MEMORY; + + /* Create a new HTTP/3 connection on the QUIC connection. */ + qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config); + if(!qs->h3c) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + if(conn->hequic[1-tempindex].cfg) { + qs = &conn->hequic[1-tempindex]; + quiche_config_free(qs->cfg); + quiche_conn_free(qs->conn); + qs->cfg = NULL; + qs->conn = NULL; + } + return CURLE_OK; + fail: + quiche_h3_config_free(qs->h3config); + quiche_h3_conn_free(qs->h3c); + return result; +} + +/* + * This function gets polled to check if this QUIC connection has connected. + */ +CURLcode Curl_quic_is_connected(struct connectdata *conn, int sockindex, + bool *done) +{ + CURLcode result; + struct quicsocket *qs = &conn->hequic[sockindex]; + curl_socket_t sockfd = conn->tempsock[sockindex]; + + result = process_ingress(conn, sockfd, qs); + if(result) + goto error; + + result = flush_egress(conn, sockfd, qs); + if(result) + goto error; + + if(quiche_conn_is_established(qs->conn)) { + *done = TRUE; + result = quiche_has_connected(conn, 0, sockindex); + DEBUGF(infof(conn->data, "quiche established connection!\n")); + } + + return result; + error: + qs_disconnect(qs); + return result; +} + +static CURLcode process_ingress(struct connectdata *conn, int sockfd, + struct quicsocket *qs) +{ + ssize_t recvd; + struct Curl_easy *data = conn->data; + uint8_t *buf = (uint8_t *)data->state.buffer; + size_t bufsize = data->set.buffer_size; + + /* in case the timeout expired */ + quiche_conn_on_timeout(qs->conn); + + do { + recvd = recv(sockfd, buf, bufsize, 0); + if((recvd < 0) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK))) + break; + + if(recvd < 0) { + failf(conn->data, "quiche: recv() unexpectedly returned %d " + "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd); + return CURLE_RECV_ERROR; + } + + recvd = quiche_conn_recv(qs->conn, buf, recvd); + if(recvd == QUICHE_ERR_DONE) + break; + + if(recvd < 0) { + failf(conn->data, "quiche_conn_recv() == %d", recvd); + return CURLE_RECV_ERROR; + } + } while(1); + + return CURLE_OK; +} + +/* + * flush_egress drains the buffers and sends off data. + * Calls failf() on errors. + */ +static CURLcode flush_egress(struct connectdata *conn, int sockfd, + struct quicsocket *qs) +{ + ssize_t sent; + static uint8_t out[1200]; + int64_t timeout_ns; + + do { + sent = quiche_conn_send(qs->conn, out, sizeof(out)); + if(sent == QUICHE_ERR_DONE) + break; + + if(sent < 0) { + failf(conn->data, "quiche_conn_send returned %zd\n", + sent); + return CURLE_SEND_ERROR; + } + + sent = send(sockfd, out, sent, 0); + if(sent < 0) { + failf(conn->data, "send() returned %zd\n", sent); + return CURLE_SEND_ERROR; + } + } while(1); + + /* time until the next timeout event, as nanoseconds. */ + timeout_ns = quiche_conn_timeout_as_nanos(qs->conn); + if(timeout_ns) + /* expire uses milliseconds */ + Curl_expire(conn->data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC); + + return CURLE_OK; +} + +struct h3h1header { + char *dest; + size_t destlen; /* left to use */ + size_t nlen; /* used */ +}; + +static int cb_each_header(uint8_t *name, size_t name_len, + uint8_t *value, size_t value_len, + void *argp) +{ + struct h3h1header *headers = (struct h3h1header *)argp; + size_t olen = 0; + + if((name_len == 7) && !strncmp(":status", (char *)name, 7)) { + msnprintf(headers->dest, + headers->destlen, "HTTP/3 %.*s\n", + (int) value_len, value); + } + else if(!headers->nlen) { + return CURLE_HTTP3; + } + else { + msnprintf(headers->dest, + headers->destlen, "%.*s: %.*s\n", + (int)name_len, name, (int) value_len, value); + } + olen = strlen(headers->dest); + headers->destlen -= olen; + headers->nlen += olen; + headers->dest += olen; + return 0; +} + +static ssize_t h3_stream_recv(struct connectdata *conn, + int sockindex, + char *buf, + size_t buffersize, + CURLcode *curlcode) +{ + ssize_t recvd = -1; + ssize_t rcode; + struct quicsocket *qs = conn->quic; + curl_socket_t sockfd = conn->sock[sockindex]; + quiche_h3_event *ev; + int rc; + struct h3h1header headers; + struct Curl_easy *data = conn->data; + struct HTTP *stream = data->req.protop; + headers.dest = buf; + headers.destlen = buffersize; + headers.nlen = 0; + + if(process_ingress(conn, sockfd, qs)) { + infof(data, "h3_stream_recv returns on ingress\n"); + *curlcode = CURLE_RECV_ERROR; + return -1; + } + + while(recvd < 0) { + int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev); + if(s < 0) + /* nothing more to do */ + break; + + if(s != stream->stream3_id) { + /* another transfer, ignore for now */ + infof(data, "Got h3 for stream %u, expects %u\n", + s, stream->stream3_id); + continue; + } + + switch(quiche_h3_event_type(ev)) { + case QUICHE_H3_EVENT_HEADERS: + rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers); + if(rc) { + *curlcode = rc; + failf(data, "Error in HTTP/3 response header"); + break; + } + recvd = headers.nlen; + break; + case QUICHE_H3_EVENT_DATA: + if(!stream->firstbody) { + /* add a header-body separator CRLF */ + buf[0] = '\r'; + buf[1] = '\n'; + buf += 2; + buffersize -= 2; + stream->firstbody = TRUE; + recvd = 2; /* two bytes already */ + } + else + recvd = 0; + rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf, + buffersize); + if(rcode <= 0) { + recvd = -1; + break; + } + recvd += rcode; + break; + + case QUICHE_H3_EVENT_FINISHED: + streamclose(conn, "End of stream"); + recvd = 0; /* end of stream */ + break; + default: + break; + } + + quiche_h3_event_free(ev); + } + if(flush_egress(conn, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + + *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK; + if(recvd >= 0) + /* Get this called again to drain the event queue */ + Curl_expire(data, 0, EXPIRE_QUIC); + + data->state.drain = (recvd >= 0) ? 1 : 0; + return recvd; +} + +static ssize_t h3_stream_send(struct connectdata *conn, + int sockindex, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + ssize_t sent; + struct quicsocket *qs = conn->quic; + curl_socket_t sockfd = conn->sock[sockindex]; + struct HTTP *stream = conn->data->req.protop; + + if(!stream->h3req) { + CURLcode result = http_request(conn, mem, len); + if(result) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + sent = len; + } + else { + H3BUGF(infof(conn->data, "Pass on %zd body bytes to quiche\n", + len)); + sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, + (uint8_t *)mem, len, FALSE); + if(sent < 0) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + } + + if(flush_egress(conn, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + + *curlcode = CURLE_OK; + return sent; +} + +/* + * Store quiche version info in this buffer, Prefix with a space. Return total + * length written. + */ +int Curl_quic_ver(char *p, size_t len) +{ + return msnprintf(p, len, "quiche/%s", quiche_version()); +} + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +static CURLcode http_request(struct connectdata *conn, const void *mem, + size_t len) +{ + /* + */ + struct HTTP *stream = conn->data->req.protop; + size_t nheader; + size_t i; + size_t authority_idx; + char *hdbuf = (char *)mem; + char *end, *line_end; + int64_t stream3_id; + quiche_h3_header *nva = NULL; + struct quicsocket *qs = conn->quic; + CURLcode result = CURLE_OK; + struct Curl_easy *data = conn->data; + + stream->h3req = TRUE; /* senf off! */ + + /* Calculate number of headers contained in [mem, mem + len). Assumes a + correctly generated HTTP header field block. */ + nheader = 0; + for(i = 1; i < len; ++i) { + if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { + ++nheader; + ++i; + } + } + if(nheader < 2) + goto fail; + + /* We counted additional 2 \r\n in the first and last line. We need 3 + new headers: :method, :path and :scheme. Therefore we need one + more space. */ + nheader += 1; + nva = malloc(sizeof(quiche_h3_header) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + /* Extract :method, :path from request line + We do line endings with CRLF so checking for CR is enough */ + line_end = memchr(hdbuf, '\r', len); + if(!line_end) { + result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */ + goto fail; + } + + /* Method does not contain spaces */ + end = memchr(hdbuf, ' ', line_end - hdbuf); + if(!end || end == hdbuf) + goto fail; + nva[0].name = (unsigned char *)":method"; + nva[0].name_len = strlen((char *)nva[0].name); + nva[0].value = (unsigned char *)hdbuf; + nva[0].value_len = (size_t)(end - hdbuf); + + hdbuf = end + 1; + + /* Path may contain spaces so scan backwards */ + end = NULL; + for(i = (size_t)(line_end - hdbuf); i; --i) { + if(hdbuf[i - 1] == ' ') { + end = &hdbuf[i - 1]; + break; + } + } + if(!end || end == hdbuf) + goto fail; + nva[1].name = (unsigned char *)":path"; + nva[1].name_len = strlen((char *)nva[1].name); + nva[1].value = (unsigned char *)hdbuf; + nva[1].value_len = (size_t)(end - hdbuf); + + nva[2].name = (unsigned char *)":scheme"; + nva[2].name_len = strlen((char *)nva[2].name); + if(conn->handler->flags & PROTOPT_SSL) + nva[2].value = (unsigned char *)"https"; + else + nva[2].value = (unsigned char *)"http"; + nva[2].value_len = strlen((char *)nva[2].value); + + + authority_idx = 0; + i = 3; + while(i < nheader) { + size_t hlen; + + hdbuf = line_end + 2; + + /* check for next CR, but only within the piece of data left in the given + buffer */ + line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); + if(!line_end || (line_end == hdbuf)) + goto fail; + + /* header continuation lines are not supported */ + if(*hdbuf == ' ' || *hdbuf == '\t') + goto fail; + + for(end = hdbuf; end < line_end && *end != ':'; ++end) + ; + if(end == hdbuf || end == line_end) + goto fail; + hlen = end - hdbuf; + + if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { + authority_idx = i; + nva[i].name = (unsigned char *)":authority"; + nva[i].name_len = strlen((char *)nva[i].name); + } + else { + nva[i].name_len = (size_t)(end - hdbuf); + /* Lower case the header name for HTTP/3 */ + Curl_strntolower((char *)hdbuf, hdbuf, nva[i].name_len); + nva[i].name = (unsigned char *)hdbuf; + } + hdbuf = end + 1; + while(*hdbuf == ' ' || *hdbuf == '\t') + ++hdbuf; + end = line_end; + +#if 0 /* This should probably go in more or less like this */ + switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, + end - hdbuf)) { + case HEADERINST_IGNORE: + /* skip header fields prohibited by HTTP/2 specification. */ + --nheader; + continue; + case HEADERINST_TE_TRAILERS: + nva[i].value = (uint8_t*)"trailers"; + nva[i].value_len = sizeof("trailers") - 1; + break; + default: + nva[i].value = (unsigned char *)hdbuf; + nva[i].value_len = (size_t)(end - hdbuf); + } +#endif + nva[i].value = (unsigned char *)hdbuf; + nva[i].value_len = (size_t)(end - hdbuf); + + ++i; + } + + /* :authority must come before non-pseudo header fields */ + if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) { + quiche_h3_header authority = nva[authority_idx]; + for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { + nva[i] = nva[i - 1]; + } + nva[i] = authority; + } + + /* Warn stream may be rejected if cumulative length of headers is too + large. */ +#define MAX_ACC 60000 /* <64KB to account for some overhead */ + { + size_t acc = 0; + + for(i = 0; i < nheader; ++i) { + acc += nva[i].name_len + nva[i].value_len; + + H3BUGF(infof(data, "h3 [%.*s: %.*s]\n", + nva[i].name_len, nva[i].name, + nva[i].value_len, nva[i].value)); + } + + if(acc > MAX_ACC) { + infof(data, "http_request: Warning: The cumulative length of all " + "headers exceeds %zu bytes and that could cause the " + "stream to be rejected.\n", MAX_ACC); + } + } + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown, but not zero */ + + stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, + stream->upload_left ? FALSE: TRUE); + if((stream3_id >= 0) && data->set.postfields) { + ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id, + (uint8_t *)data->set.postfields, + stream->upload_left, TRUE); + if(sent <= 0) { + failf(data, "quiche_h3_send_body failed!"); + result = CURLE_SEND_ERROR; + } + stream->upload_left = 0; /* nothing left to send */ + } + break; + default: + stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, + TRUE); + break; + } + + Curl_safefree(nva); + + if(stream3_id < 0) { + H3BUGF(infof(data, "quiche_h3_send_request returned %d\n", + stream3_id)); + result = CURLE_SEND_ERROR; + goto fail; + } + + infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n", + stream3_id, (void *)data); + stream->stream3_id = stream3_id; + + return CURLE_OK; + +fail: + free(nva); + return result; +} + +/* + * Called from transfer.c:done_sending when we stop HTTP/3 uploading. + */ +CURLcode Curl_quic_done_sending(struct connectdata *conn) +{ + if(conn->handler == &Curl_handler_http3) { + /* only for HTTP/3 transfers */ + ssize_t sent; + struct HTTP *stream = conn->data->req.protop; + struct quicsocket *qs = conn->quic; + fprintf(stderr, "!!! Curl_quic_done_sending\n"); + stream->upload_done = TRUE; + sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, + NULL, 0, TRUE); + if(sent < 0) + return CURLE_SEND_ERROR; + } + + return CURLE_OK; +} + +/* + * Called from http.c:Curl_http_done when a request completes. + */ +void Curl_quic_done(struct Curl_easy *data, bool premature) +{ + (void)data; + (void)premature; +} + +/* + * Called from transfer.c:data_pending to know if we should keep looping + * to receive more data from the connection. + */ +bool Curl_quic_data_pending(const struct Curl_easy *data) +{ + (void)data; + return FALSE; +} + +#endif diff --git a/Utilities/cmcurl/lib/vquic/quiche.h b/Utilities/cmcurl/lib/vquic/quiche.h new file mode 100644 index 00000000000..c8d1837b561 --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/quiche.h @@ -0,0 +1,49 @@ +#ifndef HEADER_CURL_VQUIC_QUICHE_H +#define HEADER_CURL_VQUIC_QUICHE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_QUICHE + +#include + +struct quic_handshake { + char *buf; /* pointer to the buffer */ + size_t alloclen; /* size of allocation */ + size_t len; /* size of content in buffer */ + size_t nread; /* how many bytes have been read */ +}; + +struct quicsocket { + quiche_config *cfg; + quiche_conn *conn; + quiche_h3_conn *h3c; + quiche_h3_config *h3config; + uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; + uint32_t version; +}; + +#endif + +#endif /* HEADER_CURL_VQUIC_QUICHE_H */ diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c new file mode 100644 index 00000000000..aae8e095144 --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/vquic.c @@ -0,0 +1,85 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef ENABLE_QUIC + +#ifdef HAVE_FCNTL_H +#include +#endif +#include "urldata.h" +#include "dynbuf.h" +#include "curl_printf.h" +#include "vquic.h" + +#ifdef O_BINARY +#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY +#else +#define QLOGMODE O_WRONLY|O_CREAT +#endif + +/* + * If the QLOGDIR environment variable is set, open and return a file + * descriptor to write the log to. + * + * This function returns error if something failed outside of failing to + * create the file. Open file success is deemed by seeing if the returned fd + * is != -1. + */ +CURLcode Curl_qlogdir(struct Curl_easy *data, + unsigned char *scid, + size_t scidlen, + int *qlogfdp) +{ + const char *qlog_dir = getenv("QLOGDIR"); + *qlogfdp = -1; + if(qlog_dir) { + struct dynbuf fname; + CURLcode result; + unsigned int i; + Curl_dyn_init(&fname, DYN_QLOG_NAME); + result = Curl_dyn_add(&fname, qlog_dir); + if(!result) + result = Curl_dyn_add(&fname, "/"); + for(i = 0; (i < scidlen) && !result; i++) { + char hex[3]; + msnprintf(hex, 3, "%02x", scid[i]); + result = Curl_dyn_add(&fname, hex); + } + if(!result) + result = Curl_dyn_add(&fname, ".qlog"); + + if(!result) { + int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE, + data->set.new_file_perms); + if(qlogfd != -1) + *qlogfdp = qlogfd; + } + Curl_dyn_free(&fname); + if(result) + return result; + } + + return CURLE_OK; +} +#endif diff --git a/Utilities/cmcurl/lib/vquic/vquic.h b/Utilities/cmcurl/lib/vquic/vquic.h new file mode 100644 index 00000000000..ecff0edf4ee --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/vquic.h @@ -0,0 +1,34 @@ +#ifndef HEADER_CURL_VQUIC_QUIC_H +#define HEADER_CURL_VQUIC_QUIC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef ENABLE_QUIC +CURLcode Curl_qlogdir(struct Curl_easy *data, + unsigned char *scid, + size_t scidlen, + int *qlogfdp); +#endif + +#endif /* HEADER_CURL_VQUIC_QUIC_H */ diff --git a/Utilities/cmcurl/lib/ssh-libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c similarity index 92% rename from Utilities/cmcurl/lib/ssh-libssh.c rename to Utilities/cmcurl/lib/vssh/libssh.c index 6cfd6bda8b8..8988e2392d5 100644 --- a/Utilities/cmcurl/lib/ssh-libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2017 - 2019 Red Hat, Inc. + * Copyright (C) 2017 - 2020 Red Hat, Inc. * * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek, * Robert Kolcun, Andreas Schneider @@ -97,9 +97,13 @@ /* A recent macro provided by libssh. Or make our own. */ #ifndef SSH_STRING_FREE_CHAR -/* !checksrc! disable ASSIGNWITHINCONDITION 1 */ -#define SSH_STRING_FREE_CHAR(x) \ - do { if((x) != NULL) { ssh_string_free_char(x); x = NULL; } } while(0) +#define SSH_STRING_FREE_CHAR(x) \ + do { \ + if(x) { \ + ssh_string_free_char(x); \ + x = NULL; \ + } \ + } while(0) #endif /* Local functions: */ @@ -126,13 +130,9 @@ CURLcode sftp_perform(struct connectdata *conn, static void sftp_quote(struct connectdata *conn); static void sftp_quote_stat(struct connectdata *conn); - -static int myssh_getsock(struct connectdata *conn, curl_socket_t *sock, - int numsocks); - +static int myssh_getsock(struct connectdata *conn, curl_socket_t *sock); static int myssh_perform_getsock(const struct connectdata *conn, - curl_socket_t *sock, - int numsocks); + curl_socket_t *sock); static CURLcode myssh_setup_connection(struct connectdata *conn); @@ -322,25 +322,50 @@ static int myssh_is_known(struct connectdata *conn) ssh_key pubkey; size_t hlen; unsigned char *hash = NULL; - char *base64 = NULL; + char *found_base64 = NULL; + char *known_base64 = NULL; int vstate; enum curl_khmatch keymatch; struct curl_khkey foundkey; + struct curl_khkey *knownkeyp = NULL; curl_sshkeycallback func = data->set.ssh_keyfunc; +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) + struct ssh_knownhosts_entry *knownhostsentry = NULL; + struct curl_khkey knownkey; +#endif + +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0) + rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey); +#else rc = ssh_get_publickey(sshc->ssh_session, &pubkey); +#endif if(rc != SSH_OK) return rc; if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { + int i; + char md5buffer[33]; + const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen); - if(rc != SSH_OK) + if(rc != SSH_OK || hlen != 16) { + failf(data, + "Denied establishing ssh session: md5 fingerprint not available"); goto cleanup; + } + + for(i = 0; i < 16; i++) + msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]); + + infof(data, "SSH MD5 fingerprint: %s\n", md5buffer); - if(hlen != strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) || - memcmp(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], hash, hlen)) { + if(!strcasecompare(md5buffer, pubkey_md5)) { + failf(data, + "Denied establishing ssh session: mismatch md5 fingerprint. " + "Remote %s is not equal to %s", md5buffer, pubkey_md5); rc = SSH_ERROR; goto cleanup; } @@ -354,6 +379,68 @@ static int myssh_is_known(struct connectdata *conn) goto cleanup; } +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) + /* Get the known_key from the known hosts file */ + vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session, + &knownhostsentry); + + /* Case an entry was found in a known hosts file */ + if(knownhostsentry) { + if(knownhostsentry->publickey) { + rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey, + &known_base64); + if(rc != SSH_OK) { + goto cleanup; + } + knownkey.key = known_base64; + knownkey.len = strlen(known_base64); + + switch(ssh_key_type(knownhostsentry->publickey)) { + case SSH_KEYTYPE_RSA: + knownkey.keytype = CURLKHTYPE_RSA; + break; + case SSH_KEYTYPE_RSA1: + knownkey.keytype = CURLKHTYPE_RSA1; + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_ECDSA_P256: + case SSH_KEYTYPE_ECDSA_P384: + case SSH_KEYTYPE_ECDSA_P521: + knownkey.keytype = CURLKHTYPE_ECDSA; + break; + case SSH_KEYTYPE_ED25519: + knownkey.keytype = CURLKHTYPE_ED25519; + break; + case SSH_KEYTYPE_DSS: + knownkey.keytype = CURLKHTYPE_DSS; + break; + default: + rc = SSH_ERROR; + goto cleanup; + } + knownkeyp = &knownkey; + } + } + + switch(vstate) { + case SSH_KNOWN_HOSTS_OK: + keymatch = CURLKHMATCH_OK; + break; + case SSH_KNOWN_HOSTS_OTHER: + /* fallthrough */ + case SSH_KNOWN_HOSTS_NOT_FOUND: + /* fallthrough */ + case SSH_KNOWN_HOSTS_UNKNOWN: + /* fallthrough */ + case SSH_KNOWN_HOSTS_ERROR: + keymatch = CURLKHMATCH_MISSING; + break; + default: + keymatch = CURLKHMATCH_MISMATCH; + break; + } + +#else vstate = ssh_is_server_known(sshc->ssh_session); switch(vstate) { case SSH_SERVER_KNOWN_OK: @@ -368,14 +455,15 @@ static int myssh_is_known(struct connectdata *conn) keymatch = CURLKHMATCH_MISMATCH; break; } +#endif if(func) { /* use callback to determine action */ - rc = ssh_pki_export_pubkey_base64(pubkey, &base64); + rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64); if(rc != SSH_OK) goto cleanup; - foundkey.key = base64; - foundkey.len = strlen(base64); + foundkey.key = found_base64; + foundkey.len = strlen(found_base64); switch(ssh_key_type(pubkey)) { case SSH_KEYTYPE_RSA: @@ -385,6 +473,11 @@ static int myssh_is_known(struct connectdata *conn) foundkey.keytype = CURLKHTYPE_RSA1; break; case SSH_KEYTYPE_ECDSA: +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) + case SSH_KEYTYPE_ECDSA_P256: + case SSH_KEYTYPE_ECDSA_P384: + case SSH_KEYTYPE_ECDSA_P521: +#endif foundkey.keytype = CURLKHTYPE_ECDSA; break; #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0) @@ -400,15 +493,19 @@ static int myssh_is_known(struct connectdata *conn) goto cleanup; } - /* we don't have anything equivalent to knownkey. Always NULL */ Curl_set_in_callback(data, true); - rc = func(data, NULL, &foundkey, /* from the remote host */ + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ keymatch, data->set.ssh_keyfunc_userp); Curl_set_in_callback(data, false); switch(rc) { case CURLKHSTAT_FINE_ADD_TO_FILE: +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0) + rc = ssh_session_update_known_hosts(sshc->ssh_session); +#else rc = ssh_write_knownhost(sshc->ssh_session); +#endif if(rc != SSH_OK) { goto cleanup; } @@ -429,9 +526,20 @@ static int myssh_is_known(struct connectdata *conn) rc = SSH_OK; cleanup: + if(found_base64) { + free(found_base64); + } + if(known_base64) { + free(known_base64); + } if(hash) ssh_clean_pubkey_hash(&hash); ssh_key_free(pubkey); +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) + if(knownhostsentry) { + ssh_knownhosts_entry_free(knownhostsentry); + } +#endif return rc; } @@ -497,7 +605,7 @@ int myssh_auth_interactive(struct connectdata *conn) return SSH_ERROR; nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); - if(nprompts == SSH_ERROR || nprompts != 1) + if(nprompts != 1) return SSH_ERROR; rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); @@ -1119,7 +1227,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) flags = O_WRONLY|O_APPEND; else /* Clear file before writing (normal behaviour) */ - flags = O_WRONLY|O_APPEND|O_CREAT|O_TRUNC; + flags = O_WRONLY|O_CREAT|O_TRUNC; if(sshc->sftp_file) sftp_close(sshc->sftp_file); @@ -1360,7 +1468,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) break; } } - else if(sshc->readdir_attrs == NULL && sftp_dir_eof(sshc->sftp_dir)) { + else if(sftp_dir_eof(sshc->sftp_dir)) { state(conn, SSH_SFTP_READDIR_DONE); break; } @@ -1586,7 +1694,6 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) return CURLE_BAD_DOWNLOAD_RESUME; } } - /* Does a completed file need to be seeked and started or closed ? */ /* Now store the number of bytes we are expected to download */ data->req.size = size - data->state.resume_from; data->req.maxdownload = size - data->state.resume_from; @@ -1913,13 +2020,9 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) /* called by the multi interface to figure out what socket(s) to wait for and for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ static int myssh_perform_getsock(const struct connectdata *conn, - curl_socket_t *sock, /* points to numsocks - number of sockets */ - int numsocks) + curl_socket_t *sock) { int bitmap = GETSOCK_BLANK; - (void) numsocks; - sock[0] = conn->sock[FIRSTSOCKET]; if(conn->waitfor & KEEP_RECV) @@ -1934,13 +2037,11 @@ static int myssh_perform_getsock(const struct connectdata *conn, /* Generic function called by the multi interface to figure out what socket(s) to wait for and for what actions during the DOING and PROTOCONNECT states*/ static int myssh_getsock(struct connectdata *conn, - curl_socket_t *sock, /* points to numsocks - number of sockets */ - int numsocks) + curl_socket_t *sock) { /* if we know the direction we can use the generic *_getsock() function even for the protocol_connect and doing states */ - return myssh_perform_getsock(conn, sock, numsocks); + return myssh_perform_getsock(conn, sock); } static void myssh_block2waitfor(struct connectdata *conn, bool block) @@ -1968,11 +2069,10 @@ static CURLcode myssh_multi_statemach(struct connectdata *conn, bool *done) { struct ssh_conn *sshc = &conn->proto.sshc; - CURLcode result = CURLE_OK; bool block; /* we store the status and use that to provide a ssh_getsock() implementation */ + CURLcode result = myssh_statemach_act(conn, &block); - result = myssh_statemach_act(conn, &block); *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; myssh_block2waitfor(conn, block); @@ -2010,7 +2110,7 @@ static CURLcode myssh_block_statemach(struct connectdata *conn, } } - if(!result && block) { + if(block) { curl_socket_t fd_read = conn->sock[FIRSTSOCKET]; /* wait for the socket to become ready */ (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD, @@ -2049,6 +2149,7 @@ static CURLcode myssh_connect(struct connectdata *conn, bool *done) CURLcode result; curl_socket_t sock = conn->sock[FIRSTSOCKET]; struct Curl_easy *data = conn->data; + int rc; /* initialize per-handle data if not already */ if(!data->req.protop) @@ -2075,38 +2176,70 @@ static CURLcode myssh_connect(struct connectdata *conn, bool *done) return CURLE_FAILED_INIT; } - ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock); + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name); + if(rc != SSH_OK) { + failf(data, "Could not set remote host"); + return CURLE_FAILED_INIT; + } + + rc = ssh_options_parse_config(ssh->ssh_session, NULL); + if(rc != SSH_OK) { + infof(data, "Could not parse SSH configuration files"); + /* ignore */ + } + + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock); + if(rc != SSH_OK) { + failf(data, "Could not set socket"); + return CURLE_FAILED_INIT; + } - if(conn->user) { + if(conn->user && conn->user[0] != '\0') { infof(data, "User: %s\n", conn->user); - ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user); + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user); + if(rc != SSH_OK) { + failf(data, "Could not set user"); + return CURLE_FAILED_INIT; + } } if(data->set.str[STRING_SSH_KNOWNHOSTS]) { infof(data, "Known hosts: %s\n", data->set.str[STRING_SSH_KNOWNHOSTS]); - ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS, - data->set.str[STRING_SSH_KNOWNHOSTS]); + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS, + data->set.str[STRING_SSH_KNOWNHOSTS]); + if(rc != SSH_OK) { + failf(data, "Could not set known hosts file path"); + return CURLE_FAILED_INIT; + } } - ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name); - if(conn->remote_port) - ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT, - &conn->remote_port); + if(conn->remote_port) { + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT, + &conn->remote_port); + if(rc != SSH_OK) { + failf(data, "Could not set remote port"); + return CURLE_FAILED_INIT; + } + } if(data->set.ssh_compression) { - ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION, - "zlib,zlib@openssh.com,none"); + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION, + "zlib,zlib@openssh.com,none"); + if(rc != SSH_OK) { + failf(data, "Could not set compression"); + return CURLE_FAILED_INIT; + } } ssh->privkey = NULL; ssh->pubkey = NULL; if(data->set.str[STRING_SSH_PUBLIC_KEY]) { - int rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY], - &ssh->pubkey); + rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY], + &ssh->pubkey); if(rc != SSH_OK) { failf(data, "Could not load public key file"); - /* ignore */ + return CURLE_FAILED_INIT; } } @@ -2736,5 +2869,23 @@ static void sftp_quote_stat(struct connectdata *conn) return; } +CURLcode Curl_ssh_init(void) +{ + if(ssh_init()) { + DEBUGF(fprintf(stderr, "Error: libssh_init failed\n")); + return CURLE_FAILED_INIT; + } + return CURLE_OK; +} + +void Curl_ssh_cleanup(void) +{ + (void)ssh_finalize(); +} + +size_t Curl_ssh_version(char *buffer, size_t buflen) +{ + return msnprintf(buffer, buflen, "libssh/%s", CURL_LIBSSH_VERSION); +} #endif /* USE_LIBSSH */ diff --git a/Utilities/cmcurl/lib/ssh.c b/Utilities/cmcurl/lib/vssh/libssh2.c similarity index 86% rename from Utilities/cmcurl/lib/ssh.c rename to Utilities/cmcurl/lib/vssh/libssh2.c index a265c3c9adf..555afc9ef6b 100644 --- a/Utilities/cmcurl/lib/ssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -93,19 +93,17 @@ #define HAS_STATVFS_SUPPORT 1 #endif -#define sftp_libssh2_last_error(s) curlx_ultosi(libssh2_sftp_last_error(s)) - -#define sftp_libssh2_realpath(s,p,t,m) \ - libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \ - (t), (m), LIBSSH2_SFTP_REALPATH) - +#define sftp_libssh2_realpath(s,p,t,m) \ + libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \ + (t), (m), LIBSSH2_SFTP_REALPATH) /* Local functions: */ -static const char *sftp_libssh2_strerror(int err); +static const char *sftp_libssh2_strerror(unsigned long err); static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); static LIBSSH2_FREE_FUNC(my_libssh2_free); +static CURLcode ssh_force_knownhost_key_type(struct connectdata *conn); static CURLcode ssh_connect(struct connectdata *conn, bool *done); static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done); static CURLcode ssh_do(struct connectdata *conn, bool *done); @@ -125,17 +123,9 @@ static CURLcode sftp_perform(struct connectdata *conn, bool *connected, bool *dophase_done); - -static int ssh_getsock(struct connectdata *conn, - curl_socket_t *sock, /* points to numsocks number - of sockets */ - int numsocks); - +static int ssh_getsock(struct connectdata *conn, curl_socket_t *sock); static int ssh_perform_getsock(const struct connectdata *conn, - curl_socket_t *sock, /* points to numsocks - number of sockets */ - int numsocks); - + curl_socket_t *sock); static CURLcode ssh_setup_connection(struct connectdata *conn); /* @@ -220,7 +210,7 @@ kbd_callback(const char *name, int name_len, const char *instruction, (void)abstract; } /* kbd_callback */ -static CURLcode sftp_libssh2_error_to_CURLE(int err) +static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err) { switch(err) { case LIBSSH2_FX_OK: @@ -474,61 +464,95 @@ static CURLcode ssh_knownhost(struct connectdata *conn) struct curl_khkey *knownkeyp = NULL; struct curl_khkey foundkey; - keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? - LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS; - + switch(keytype) { + case LIBSSH2_HOSTKEY_TYPE_RSA: + keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; + break; + case LIBSSH2_HOSTKEY_TYPE_DSS: + keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; + break; +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519 + case LIBSSH2_HOSTKEY_TYPE_ED25519: + keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; + break; +#endif + default: + infof(data, "unsupported key type, can't check knownhosts!\n"); + keybit = 0; + break; + } + if(!keybit) + /* no check means failure! */ + rc = CURLKHSTAT_REJECT; + else { #ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP - keycheck = libssh2_knownhost_checkp(sshc->kh, - conn->host.name, - (conn->remote_port != PORT_SSH)? - conn->remote_port:-1, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, - &host); + keycheck = libssh2_knownhost_checkp(sshc->kh, + conn->host.name, + (conn->remote_port != PORT_SSH)? + conn->remote_port:-1, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); #else - keycheck = libssh2_knownhost_check(sshc->kh, - conn->host.name, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, - &host); + keycheck = libssh2_knownhost_check(sshc->kh, + conn->host.name, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); #endif - infof(data, "SSH host check: %d, key: %s\n", keycheck, - (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? - host->key:""); + infof(data, "SSH host check: %d, key: %s\n", keycheck, + (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? + host->key:""); + + /* setup 'knownkey' */ + if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { + knownkey.key = host->key; + knownkey.len = 0; + knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? + CURLKHTYPE_RSA : CURLKHTYPE_DSS; + knownkeyp = &knownkey; + } - /* setup 'knownkey' */ - if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { - knownkey.key = host->key; - knownkey.len = 0; - knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? + /* setup 'foundkey' */ + foundkey.key = remotekey; + foundkey.len = keylen; + foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? CURLKHTYPE_RSA : CURLKHTYPE_DSS; - knownkeyp = &knownkey; - } - /* setup 'foundkey' */ - foundkey.key = remotekey; - foundkey.len = keylen; - foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? - CURLKHTYPE_RSA : CURLKHTYPE_DSS; + /* + * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the + * curl_khmatch enum are ever modified, we need to introduce a + * translation table here! + */ + keymatch = (enum curl_khmatch)keycheck; - /* - * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the - * curl_khmatch enum are ever modified, we need to introduce a - * translation table here! - */ - keymatch = (enum curl_khmatch)keycheck; - - /* Ask the callback how to behave */ - Curl_set_in_callback(data, true); - rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ - keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, false); + /* Ask the callback how to behave */ + Curl_set_in_callback(data, true); + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + Curl_set_in_callback(data, false); + } } else /* no remotekey means failure! */ @@ -622,6 +646,138 @@ static CURLcode ssh_check_fingerprint(struct connectdata *conn) return ssh_knownhost(conn); } +/* + * ssh_force_knownhost_key_type() will check 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 connectdata *conn) +{ + CURLcode result = CURLE_OK; + +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + +#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 + static const char * const hostkey_method_ssh_ed25519 + = "ssh-ed25519"; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 + static const char * const hostkey_method_ssh_ecdsa_521 + = "ecdsa-sha2-nistp521"; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 + static const char * const hostkey_method_ssh_ecdsa_384 + = "ecdsa-sha2-nistp384"; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 + static const char * const hostkey_method_ssh_ecdsa_256 + = "ecdsa-sha2-nistp256"; +#endif + static const char * const hostkey_method_ssh_rsa + = "ssh-rsa"; + static const char * const hostkey_method_ssh_dss + = "ssh-dss"; + + const char *hostkey_method = NULL; + struct ssh_conn *sshc = &conn->proto.sshc; + struct Curl_easy *data = conn->data; + struct libssh2_knownhost* store = NULL; + const char *kh_name_end = NULL; + size_t kh_name_size = 0; + int port = 0; + bool found = false; + + if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { + /* 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 */ + /* square brackets, followed by a colon and the port */ + if(store) { + if(store->name) { + if(store->name[0] == '[') { + kh_name_end = strstr(store->name, "]:"); + if(!kh_name_end) { + infof(data, "Invalid host pattern %s in %s\n", + store->name, data->set.str[STRING_SSH_KNOWNHOSTS]); + continue; + } + port = atoi(kh_name_end + 2); + if(kh_name_end && (port == conn->remote_port)) { + kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end); + if(strncmp(store->name + 1, + conn->host.name, kh_name_size) == 0) { + found = true; + break; + } + } + } + else if(strcmp(store->name, conn->host.name) == 0) { + found = true; + break; + } + } + else { + found = true; + break; + } + } + } + + if(found) { + infof(data, "Found host %s in %s\n", + conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); + + switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { +#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 + case LIBSSH2_KNOWNHOST_KEY_ED25519: + hostkey_method = hostkey_method_ssh_ed25519; + break; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 + case LIBSSH2_KNOWNHOST_KEY_ECDSA_521: + hostkey_method = hostkey_method_ssh_ecdsa_521; + break; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 + case LIBSSH2_KNOWNHOST_KEY_ECDSA_384: + hostkey_method = hostkey_method_ssh_ecdsa_384; + break; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 + case LIBSSH2_KNOWNHOST_KEY_ECDSA_256: + hostkey_method = hostkey_method_ssh_ecdsa_256; + break; +#endif + case LIBSSH2_KNOWNHOST_KEY_SSHRSA: + hostkey_method = hostkey_method_ssh_rsa; + break; + case LIBSSH2_KNOWNHOST_KEY_SSHDSS: + hostkey_method = hostkey_method_ssh_dss; + break; + case LIBSSH2_KNOWNHOST_KEY_RSA1: + failf(data, "Found host key type RSA1 which is not supported\n"); + return CURLE_SSH; + default: + failf(data, "Unknown host key type: %i\n", + (store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK)); + return CURLE_SSH; + } + + infof(data, "Set \"%s\" as SSH hostkey type\n", hostkey_method); + result = libssh2_session_error_to_CURLE( + libssh2_session_method_pref( + sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method)); + } + else { + infof(data, "Did not find host %s in %s\n", + conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); + } + } + +#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ + + return result; +} + /* * ssh_statemach_act() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points @@ -636,10 +792,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) struct SSHPROTO *sftp_scp = data->req.protop; struct ssh_conn *sshc = &conn->proto.sshc; curl_socket_t sock = conn->sock[FIRSTSOCKET]; - char *new_readdir_line; int rc = LIBSSH2_ERROR_NONE; - int err; + int ssherr; + unsigned long sftperr; int seekerr = CURL_SEEKFUNC_OK; + size_t readdir_len; *block = 0; /* we're not blocking by default */ do { @@ -654,6 +811,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) non-blocking */ libssh2_session_set_blocking(sshc->ssh_session, 0); + result = ssh_force_knownhost_key_type(conn); + if(result) { + state(conn, SSH_SESSION_FREE); + break; + } + state(conn, SSH_S_STARTUP); /* FALLTHROUGH */ @@ -709,12 +872,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) state(conn, SSH_AUTH_DONE); break; } - err = libssh2_session_last_errno(sshc->ssh_session); - if(err == LIBSSH2_ERROR_EAGAIN) + ssherr = libssh2_session_last_errno(sshc->ssh_session); + if(ssherr == LIBSSH2_ERROR_EAGAIN) rc = LIBSSH2_ERROR_EAGAIN; else { state(conn, SSH_SESSION_FREE); - sshc->actualcode = libssh2_session_error_to_CURLE(err); + sshc->actualcode = libssh2_session_error_to_CURLE(ssherr); } break; } @@ -1084,16 +1247,16 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } else { /* Return the error type */ - err = sftp_libssh2_last_error(sshc->sftp_session); - if(err) - result = sftp_libssh2_error_to_CURLE(err); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + if(sftperr) + result = sftp_libssh2_error_to_CURLE(sftperr); else /* in this case, the error wasn't in the SFTP level but for example a time-out or similar */ result = CURLE_SSH; sshc->actualcode = result; DEBUGF(infof(data, "error = %d makes libcurl = %d\n", - err, (int)result)); + ssherr, (int)result)); state(conn, SSH_STOP); break; } @@ -1195,7 +1358,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) */ cp = strchr(cmd, ' '); if(cp == NULL) { - failf(data, "Syntax error in SFTP command. Supply parameter(s)!"); + failf(data, "Syntax error command '%s'. Missing parameter!", + cmd); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1211,7 +1375,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(result == CURLE_OUT_OF_MEMORY) failf(data, "Out of memory"); else - failf(data, "Syntax error: Bad first parameter"); + failf(data, "Syntax error: Bad first parameter to '%s'", cmd); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; @@ -1236,8 +1400,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(result == CURLE_OUT_OF_MEMORY) failf(data, "Out of memory"); else - failf(data, "Syntax error in chgrp/chmod/chown: " - "Bad second parameter"); + failf(data, "Syntax error in %s: Bad second parameter", cmd); Curl_safefree(sshc->quote_path1); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; @@ -1368,11 +1531,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc != 0 && !sshc->acceptfail) { /* get those attributes */ - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); failf(data, "Attempt to get SFTP stats failed: %s", - sftp_libssh2_strerror(err)); + sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1439,11 +1602,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc != 0 && !sshc->acceptfail) { - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); failf(data, "Attempt to set SFTP stats failed: %s", - sftp_libssh2_strerror(err)); + sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1462,11 +1625,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc != 0 && !sshc->acceptfail) { - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); failf(data, "symlink command failed: %s", - sftp_libssh2_strerror(err)); + sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1483,9 +1646,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc != 0 && !sshc->acceptfail) { - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err)); + failf(data, "mkdir command failed: %s", + sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1507,10 +1671,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc != 0 && !sshc->acceptfail) { - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); - failf(data, "rename command failed: %s", sftp_libssh2_strerror(err)); + failf(data, "rename command failed: %s", + sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1526,9 +1691,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc != 0 && !sshc->acceptfail) { - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err)); + failf(data, "rmdir command failed: %s", + sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1544,9 +1710,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc != 0 && !sshc->acceptfail) { - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - failf(data, "rm command failed: %s", sftp_libssh2_strerror(err)); + failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1567,9 +1733,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc != 0 && !sshc->acceptfail) { - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - failf(data, "statvfs command failed: %s", sftp_libssh2_strerror(err)); + failf(data, "statvfs command failed: %s", + sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1706,21 +1873,21 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc) /* only when there was an SFTP protocol error can we extract the sftp error! */ - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); else - err = -1; /* not an sftp error at all */ + sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */ if(sshc->secondCreateDirs) { state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = err>= LIBSSH2_FX_OK? - sftp_libssh2_error_to_CURLE(err):CURLE_SSH; + sshc->actualcode = sftperr != LIBSSH2_FX_OK ? + sftp_libssh2_error_to_CURLE(sftperr):CURLE_SSH; failf(data, "Creating the dir/file failed: %s", - sftp_libssh2_strerror(err)); + sftp_libssh2_strerror(sftperr)); break; } - if(((err == LIBSSH2_FX_NO_SUCH_FILE) || - (err == LIBSSH2_FX_FAILURE) || - (err == LIBSSH2_FX_NO_SUCH_PATH)) && + if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) || + (sftperr == LIBSSH2_FX_FAILURE) || + (sftperr == LIBSSH2_FX_NO_SUCH_PATH)) && (data->set.ftp_create_missing_dirs && (strlen(sftp_scp->path) > 1))) { /* try to create the path remotely */ @@ -1730,18 +1897,19 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = err>= LIBSSH2_FX_OK? - sftp_libssh2_error_to_CURLE(err):CURLE_SSH; + sshc->actualcode = sftperr != LIBSSH2_FX_OK ? + sftp_libssh2_error_to_CURLE(sftperr):CURLE_SSH; if(!sshc->actualcode) { - /* Sometimes, for some reason libssh2_sftp_last_error() returns - zero even though libssh2_sftp_open() failed previously! We need - to work around that! */ + /* Sometimes, for some reason libssh2_sftp_last_error() returns zero + even though libssh2_sftp_open() failed previously! We need to + work around that! */ sshc->actualcode = CURLE_SSH; - err = -1; + sftperr = LIBSSH2_FX_OK; } failf(data, "Upload failed: %s (%d/%d)", - err>= LIBSSH2_FX_OK?sftp_libssh2_strerror(err):"ssh error", - err, rc); + sftperr != LIBSSH2_FX_OK ? + sftp_libssh2_strerror(sftperr):"ssh error", + sftperr, rc); break; } @@ -1868,11 +2036,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) * permission was denied (creation might succeed further down the * path) - retry on unspecific FAILURE also */ - err = sftp_libssh2_last_error(sshc->sftp_session); - if((err != LIBSSH2_FX_FILE_ALREADY_EXISTS) && - (err != LIBSSH2_FX_FAILURE) && - (err != LIBSSH2_FX_PERMISSION_DENIED)) { - result = sftp_libssh2_error_to_CURLE(err); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) && + (sftperr != LIBSSH2_FX_FAILURE) && + (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) { + result = sftp_libssh2_error_to_CURLE(sftperr); state(conn, SSH_SFTP_CLOSE); sshc->actualcode = result?result:CURLE_SSH; break; @@ -1904,11 +2072,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) rc = LIBSSH2_ERROR_EAGAIN; break; } - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "Could not open directory for reading: %s", - sftp_libssh2_strerror(err)); + sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); - result = sftp_libssh2_error_to_CURLE(err); + result = sftp_libssh2_error_to_CURLE(sftperr); sshc->actualcode = result?result:CURLE_SSH; break; } @@ -1925,6 +2093,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->actualcode = CURLE_OUT_OF_MEMORY; break; } + Curl_dyn_init(&sshc->readdir, PATH_MAX * 2); state(conn, SSH_SFTP_READDIR); break; @@ -1939,68 +2108,51 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc > 0) { - sshc->readdir_len = (size_t) rc; - sshc->readdir_filename[sshc->readdir_len] = '\0'; + readdir_len = (size_t) rc; + sshc->readdir_filename[readdir_len] = '\0'; if(data->set.ftp_list_only) { - char *tmpLine; - - tmpLine = aprintf("%s\n", sshc->readdir_filename); - if(tmpLine == NULL) { - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } result = Curl_client_write(conn, CLIENTWRITE_BODY, - tmpLine, sshc->readdir_len + 1); - free(tmpLine); - + sshc->readdir_filename, + readdir_len); + if(!result) + result = Curl_client_write(conn, CLIENTWRITE_BODY, + (char *)"\n", 1); if(result) { state(conn, SSH_STOP); break; } /* since this counts what we send to the client, we include the newline in this counter */ - data->req.bytecount += sshc->readdir_len + 1; + data->req.bytecount += readdir_len + 1; /* output debug output if that is requested */ if(data->set.verbose) { - Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename, - sshc->readdir_len); + Curl_debug(data, CURLINFO_DATA_IN, sshc->readdir_filename, + readdir_len); + Curl_debug(data, CURLINFO_DATA_IN, (char *)"\n", 1); } } else { - sshc->readdir_currLen = strlen(sshc->readdir_longentry); - sshc->readdir_totalLen = 80 + sshc->readdir_currLen; - sshc->readdir_line = calloc(sshc->readdir_totalLen, 1); - if(!sshc->readdir_line) { - Curl_safefree(sshc->readdir_filename); - Curl_safefree(sshc->readdir_longentry); - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - - memcpy(sshc->readdir_line, sshc->readdir_longentry, - sshc->readdir_currLen); - if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) && - ((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) == - LIBSSH2_SFTP_S_IFLNK)) { - sshc->readdir_linkPath = malloc(PATH_MAX + 1); - if(sshc->readdir_linkPath == NULL) { - Curl_safefree(sshc->readdir_filename); - Curl_safefree(sshc->readdir_longentry); - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; + result = Curl_dyn_add(&sshc->readdir, sshc->readdir_longentry); + + if(!result) { + if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) && + ((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFLNK)) { + Curl_dyn_init(&sshc->readdir_link, PATH_MAX); + result = Curl_dyn_add(&sshc->readdir_link, sftp_scp->path); + state(conn, SSH_SFTP_READDIR_LINK); + if(!result) + break; + } + else { + state(conn, SSH_SFTP_READDIR_BOTTOM); break; } - - msnprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path, - sshc->readdir_filename); - state(conn, SSH_SFTP_READDIR_LINK); - break; } - state(conn, SSH_SFTP_READDIR_BOTTOM); + sshc->actualcode = result; + state(conn, SSH_SFTP_CLOSE); break; } } @@ -2011,11 +2163,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } else if(rc < 0) { - err = sftp_libssh2_last_error(sshc->sftp_session); - result = sftp_libssh2_error_to_CURLE(err); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + result = sftp_libssh2_error_to_CURLE(sftperr); sshc->actualcode = result?result:CURLE_SSH; failf(data, "Could not open remote file for reading: %s :: %d", - sftp_libssh2_strerror(err), + sftp_libssh2_strerror(sftperr), libssh2_session_last_errno(sshc->ssh_session)); Curl_safefree(sshc->readdir_filename); Curl_safefree(sshc->readdir_longentry); @@ -2027,64 +2179,56 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) case SSH_SFTP_READDIR_LINK: rc = libssh2_sftp_symlink_ex(sshc->sftp_session, - sshc->readdir_linkPath, - curlx_uztoui(strlen(sshc->readdir_linkPath)), + Curl_dyn_ptr(&sshc->readdir_link), + (int)Curl_dyn_len(&sshc->readdir_link), sshc->readdir_filename, PATH_MAX, LIBSSH2_SFTP_READLINK); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - sshc->readdir_len = (size_t) rc; - Curl_safefree(sshc->readdir_linkPath); + readdir_len = (size_t) rc; + Curl_dyn_free(&sshc->readdir_link); - /* get room for the filename and extra output */ - sshc->readdir_totalLen += 4 + sshc->readdir_len; - new_readdir_line = Curl_saferealloc(sshc->readdir_line, - sshc->readdir_totalLen); - if(!new_readdir_line) { + /* append filename and extra output */ + result = Curl_dyn_addf(&sshc->readdir, " -> %s", sshc->readdir_filename); + + if(result) { sshc->readdir_line = NULL; Curl_safefree(sshc->readdir_filename); Curl_safefree(sshc->readdir_longentry); state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; + sshc->actualcode = result; break; } - sshc->readdir_line = new_readdir_line; - - sshc->readdir_currLen += msnprintf(sshc->readdir_line + - sshc->readdir_currLen, - sshc->readdir_totalLen - - sshc->readdir_currLen, - " -> %s", - sshc->readdir_filename); state(conn, SSH_SFTP_READDIR_BOTTOM); break; case SSH_SFTP_READDIR_BOTTOM: - sshc->readdir_currLen += msnprintf(sshc->readdir_line + - sshc->readdir_currLen, - sshc->readdir_totalLen - - sshc->readdir_currLen, "\n"); - result = Curl_client_write(conn, CLIENTWRITE_BODY, - sshc->readdir_line, - sshc->readdir_currLen); + result = Curl_dyn_addn(&sshc->readdir, "\n", 1); + if(!result) + result = Curl_client_write(conn, CLIENTWRITE_BODY, + Curl_dyn_ptr(&sshc->readdir), + Curl_dyn_len(&sshc->readdir)); if(!result) { /* output debug output if that is requested */ if(data->set.verbose) { - Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line, - sshc->readdir_currLen); + Curl_debug(data, CURLINFO_DATA_IN, + Curl_dyn_ptr(&sshc->readdir), + Curl_dyn_len(&sshc->readdir)); } - data->req.bytecount += sshc->readdir_currLen; + data->req.bytecount += Curl_dyn_len(&sshc->readdir); } - Curl_safefree(sshc->readdir_line); if(result) { + Curl_dyn_free(&sshc->readdir); state(conn, SSH_STOP); } - else + else { + Curl_dyn_reset(&sshc->readdir); state(conn, SSH_SFTP_READDIR); + } break; case SSH_SFTP_READDIR_DONE: @@ -2117,11 +2261,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) rc = LIBSSH2_ERROR_EAGAIN; break; } - err = sftp_libssh2_last_error(sshc->sftp_session); + sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "Could not open remote file for reading: %s", - sftp_libssh2_strerror(err)); + sftp_libssh2_strerror(sftperr)); state(conn, SSH_SFTP_CLOSE); - result = sftp_libssh2_error_to_CURLE(err); + result = sftp_libssh2_error_to_CURLE(sftperr); sshc->actualcode = result?result:CURLE_SSH; break; } @@ -2225,7 +2369,6 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) return CURLE_BAD_DOWNLOAD_RESUME; } } - /* Does a completed file need to be seeked and started or closed ? */ /* Now store the number of bytes we are expected to download */ data->req.size = attrs.filesize - data->state.resume_from; data->req.maxdownload = attrs.filesize - data->state.resume_from; @@ -2387,7 +2530,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } /* upload data */ - Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET); + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); /* not set by Curl_setup_transfer to preserve keepon bits */ conn->sockfd = conn->writesockfd; @@ -2457,7 +2602,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* download data */ bytecount = (curl_off_t)sb.st_size; - data->req.maxdownload = (curl_off_t)sb.st_size; + data->req.maxdownload = (curl_off_t)sb.st_size; Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1); /* not set by Curl_setup_transfer to preserve keepon bits */ @@ -2664,7 +2809,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) Curl_safefree(sshc->readdir_filename); Curl_safefree(sshc->readdir_longentry); Curl_safefree(sshc->readdir_line); - Curl_safefree(sshc->readdir_linkPath); + Curl_dyn_free(&sshc->readdir); /* the code we are about to return */ result = sshc->actualcode; @@ -2700,13 +2845,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* called by the multi interface to figure out what socket(s) to wait for and for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ static int ssh_perform_getsock(const struct connectdata *conn, - curl_socket_t *sock, /* points to numsocks - number of sockets */ - int numsocks) + curl_socket_t *sock) { #ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION int bitmap = GETSOCK_BLANK; - (void)numsocks; sock[0] = conn->sock[FIRSTSOCKET]; @@ -2720,28 +2862,25 @@ static int ssh_perform_getsock(const struct connectdata *conn, #else /* if we don't know the direction we can use the generic *_getsock() function even for the protocol_connect and doing states */ - return Curl_single_getsock(conn, sock, numsocks); + return Curl_single_getsock(conn, sock); #endif } /* Generic function called by the multi interface to figure out what socket(s) to wait for and for what actions during the DOING and PROTOCONNECT states*/ static int ssh_getsock(struct connectdata *conn, - curl_socket_t *sock, /* points to numsocks number - of sockets */ - int numsocks) + curl_socket_t *sock) { #ifndef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION (void)conn; (void)sock; - (void)numsocks; /* if we don't know any direction we can just play along as we used to and not provide any sensible info */ return GETSOCK_BLANK; #else /* if we know the direction we can use the generic *_getsock() function even for the protocol_connect and doing states */ - return ssh_perform_getsock(conn, sock, numsocks); + return ssh_perform_getsock(conn, sock); #endif } @@ -2794,7 +2933,7 @@ static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done) } static CURLcode ssh_block_statemach(struct connectdata *conn, - bool disconnect) + bool duringconnect) { struct ssh_conn *sshc = &conn->proto.sshc; CURLcode result = CURLE_OK; @@ -2809,23 +2948,21 @@ static CURLcode ssh_block_statemach(struct connectdata *conn, if(result) break; - if(!disconnect) { - if(Curl_pgrsUpdate(conn)) - return CURLE_ABORTED_BY_CALLBACK; + if(Curl_pgrsUpdate(conn)) + return CURLE_ABORTED_BY_CALLBACK; - result = Curl_speedcheck(data, now); - if(result) - break; + result = Curl_speedcheck(data, now); + if(result) + break; - left = Curl_timeleft(data, NULL, FALSE); - if(left < 0) { - failf(data, "Operation timed out"); - return CURLE_OPERATION_TIMEDOUT; - } + left = Curl_timeleft(data, NULL, duringconnect); + if(left < 0) { + failf(data, "Operation timed out"); + return CURLE_OPERATION_TIMEDOUT; } #ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION - if(!result && block) { + if(block) { int dir = libssh2_session_block_directions(sshc->ssh_session); curl_socket_t sock = conn->sock[FIRSTSOCKET]; curl_socket_t fd_read = CURL_SOCKET_BAD; @@ -2836,7 +2973,7 @@ static CURLcode ssh_block_statemach(struct connectdata *conn, fd_write = sock; /* wait for the socket to become ready */ (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, - left>1000?1000:left); /* ignore result */ + left>1000?1000:left); } #endif @@ -3044,7 +3181,7 @@ static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection) state(conn, SSH_SESSION_DISCONNECT); - result = ssh_block_statemach(conn, TRUE); + result = ssh_block_statemach(conn, FALSE); } return result; @@ -3193,7 +3330,7 @@ static CURLcode sftp_disconnect(struct connectdata *conn, bool dead_connection) if(conn->proto.sshc.ssh_session) { /* only if there's a session still around to use! */ state(conn, SSH_SFTP_SHUTDOWN); - result = ssh_block_statemach(conn, TRUE); + result = ssh_block_statemach(conn, FALSE); } DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n")); @@ -3268,7 +3405,7 @@ static ssize_t sftp_recv(struct connectdata *conn, int sockindex, return nread; } -static const char *sftp_libssh2_strerror(int err) +static const char *sftp_libssh2_strerror(unsigned long err) { switch(err) { case LIBSSH2_FX_NO_SUCH_FILE: @@ -3334,4 +3471,27 @@ static const char *sftp_libssh2_strerror(int err) return "Unknown error in libssh2"; } +CURLcode Curl_ssh_init(void) +{ +#ifdef HAVE_LIBSSH2_INIT + if(libssh2_init(0)) { + DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n")); + return CURLE_FAILED_INIT; + } +#endif + return CURLE_OK; +} + +void Curl_ssh_cleanup(void) +{ +#ifdef HAVE_LIBSSH2_EXIT + (void)libssh2_exit(); +#endif +} + +size_t Curl_ssh_version(char *buffer, size_t buflen) +{ + return msnprintf(buffer, buflen, "libssh2/%s", LIBSSH2_VERSION); +} + #endif /* USE_LIBSSH2 */ diff --git a/Utilities/cmcurl/lib/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h similarity index 93% rename from Utilities/cmcurl/lib/ssh.h rename to Utilities/cmcurl/lib/vssh/ssh.h index 0620aac3286..9e49993e96f 100644 --- a/Utilities/cmcurl/lib/ssh.h +++ b/Utilities/cmcurl/lib/vssh/ssh.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,7 +30,10 @@ #elif defined(HAVE_LIBSSH_LIBSSH_H) #include #include -#endif /* HAVE_LIBSSH2_H */ +#elif defined(USE_WOLFSSH) +#include +#include +#endif /**************************************************************************** * SSH unique setup @@ -131,9 +134,7 @@ struct ssh_conn { quote command fails) */ char *homedir; /* when doing SFTP we figure out home dir in the connect phase */ - size_t readdir_len, readdir_totalLen, readdir_currLen; char *readdir_line; - char *readdir_linkPath; /* end of READDIR stuff */ int secondCreateDirs; /* counter use by the code to see if the @@ -144,6 +145,8 @@ struct ssh_conn { int orig_waitfor; /* default READ/WRITE bits wait for */ #if defined(USE_LIBSSH) + char *readdir_linkPath; + size_t readdir_len, readdir_totalLen, readdir_currLen; /* our variables */ unsigned kbd_state; /* 0 or 1 */ ssh_key privkey; @@ -165,6 +168,8 @@ struct ssh_conn { const char *readdir_longentry; char *readdir_tmp; #elif defined(USE_LIBSSH2) + struct dynbuf readdir_link; + struct dynbuf readdir; char *readdir_filename; char *readdir_longentry; @@ -188,6 +193,12 @@ struct ssh_conn { #ifdef HAVE_LIBSSH2_KNOWNHOST_API LIBSSH2_KNOWNHOSTS *kh; #endif +#elif defined(USE_WOLFSSH) + WOLFSSH *ssh_session; + WOLFSSH_CTX *ctx; + word32 handleSz; + byte handle[WOLFSSH_MAX_HANDLE]; + curl_off_t offset; #endif /* USE_LIBSSH */ }; @@ -195,9 +206,6 @@ struct ssh_conn { #define CURL_LIBSSH_VERSION ssh_version(0) -extern const struct Curl_handler Curl_handler_scp; -extern const struct Curl_handler Curl_handler_sftp; - #elif defined(USE_LIBSSH2) /* Feature detection based on version numbers to better work with @@ -237,9 +245,20 @@ extern const struct Curl_handler Curl_handler_sftp; #define CURL_LIBSSH2_VERSION LIBSSH2_VERSION #endif +#endif /* USE_LIBSSH2 */ + +#ifdef USE_SSH + extern const struct Curl_handler Curl_handler_scp; extern const struct Curl_handler Curl_handler_sftp; -#endif /* USE_LIBSSH2 */ +/* generic SSH backend functions */ +CURLcode Curl_ssh_init(void); +void Curl_ssh_cleanup(void); +size_t Curl_ssh_version(char *buffer, size_t buflen); +#else +/* for non-SSH builds */ +#define Curl_ssh_cleanup() +#endif #endif /* HEADER_CURL_SSH_H */ diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c new file mode 100644 index 00000000000..dcbbab6c3de --- /dev/null +++ b/Utilities/cmcurl/lib/vssh/wolfssh.c @@ -0,0 +1,1156 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2019 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_WOLFSSH + +#include + +#include +#include +#include "urldata.h" +#include "connect.h" +#include "sendf.h" +#include "progress.h" +#include "curl_path.h" +#include "strtoofft.h" +#include "transfer.h" +#include "speedcheck.h" +#include "select.h" +#include "multiif.h" +#include "warnless.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +static CURLcode wssh_connect(struct connectdata *conn, bool *done); +static CURLcode wssh_multi_statemach(struct connectdata *conn, bool *done); +static CURLcode wssh_do(struct connectdata *conn, bool *done); +#if 0 +static CURLcode wscp_done(struct connectdata *conn, + CURLcode, bool premature); +static CURLcode wscp_doing(struct connectdata *conn, + bool *dophase_done); +static CURLcode wscp_disconnect(struct connectdata *conn, + bool dead_connection); +#endif +static CURLcode wsftp_done(struct connectdata *conn, + CURLcode, bool premature); +static CURLcode wsftp_doing(struct connectdata *conn, + bool *dophase_done); +static CURLcode wsftp_disconnect(struct connectdata *conn, bool dead); +static int wssh_getsock(struct connectdata *conn, + curl_socket_t *sock); +static int wssh_perform_getsock(const struct connectdata *conn, + curl_socket_t *sock); +static CURLcode wssh_setup_connection(struct connectdata *conn); + +#if 0 +/* + * SCP protocol handler. + */ + +const struct Curl_handler Curl_handler_scp = { + "SCP", /* scheme */ + wssh_setup_connection, /* setup_connection */ + wssh_do, /* do_it */ + wscp_done, /* done */ + ZERO_NULL, /* do_more */ + wssh_connect, /* connect_it */ + wssh_multi_statemach, /* connecting */ + wscp_doing, /* doing */ + wssh_getsock, /* proto_getsock */ + wssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + wssh_perform_getsock, /* perform_getsock */ + wscp_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ + PORT_SSH, /* defport */ + CURLPROTO_SCP, /* protocol */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ +}; + +#endif + +/* + * SFTP protocol handler. + */ + +const struct Curl_handler Curl_handler_sftp = { + "SFTP", /* scheme */ + wssh_setup_connection, /* setup_connection */ + wssh_do, /* do_it */ + wsftp_done, /* done */ + ZERO_NULL, /* do_more */ + wssh_connect, /* connect_it */ + wssh_multi_statemach, /* connecting */ + wsftp_doing, /* doing */ + wssh_getsock, /* proto_getsock */ + wssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + wssh_perform_getsock, /* perform_getsock */ + wsftp_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ + PORT_SSH, /* defport */ + CURLPROTO_SFTP, /* protocol */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ +}; + +/* + * SSH State machine related code + */ +/* This is the ONLY way to change SSH state! */ +static void state(struct connectdata *conn, sshstate nowstate) +{ + struct ssh_conn *sshc = &conn->proto.sshc; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[] = { + "SSH_STOP", + "SSH_INIT", + "SSH_S_STARTUP", + "SSH_HOSTKEY", + "SSH_AUTHLIST", + "SSH_AUTH_PKEY_INIT", + "SSH_AUTH_PKEY", + "SSH_AUTH_PASS_INIT", + "SSH_AUTH_PASS", + "SSH_AUTH_AGENT_INIT", + "SSH_AUTH_AGENT_LIST", + "SSH_AUTH_AGENT", + "SSH_AUTH_HOST_INIT", + "SSH_AUTH_HOST", + "SSH_AUTH_KEY_INIT", + "SSH_AUTH_KEY", + "SSH_AUTH_GSSAPI", + "SSH_AUTH_DONE", + "SSH_SFTP_INIT", + "SSH_SFTP_REALPATH", + "SSH_SFTP_QUOTE_INIT", + "SSH_SFTP_POSTQUOTE_INIT", + "SSH_SFTP_QUOTE", + "SSH_SFTP_NEXT_QUOTE", + "SSH_SFTP_QUOTE_STAT", + "SSH_SFTP_QUOTE_SETSTAT", + "SSH_SFTP_QUOTE_SYMLINK", + "SSH_SFTP_QUOTE_MKDIR", + "SSH_SFTP_QUOTE_RENAME", + "SSH_SFTP_QUOTE_RMDIR", + "SSH_SFTP_QUOTE_UNLINK", + "SSH_SFTP_QUOTE_STATVFS", + "SSH_SFTP_GETINFO", + "SSH_SFTP_FILETIME", + "SSH_SFTP_TRANS_INIT", + "SSH_SFTP_UPLOAD_INIT", + "SSH_SFTP_CREATE_DIRS_INIT", + "SSH_SFTP_CREATE_DIRS", + "SSH_SFTP_CREATE_DIRS_MKDIR", + "SSH_SFTP_READDIR_INIT", + "SSH_SFTP_READDIR", + "SSH_SFTP_READDIR_LINK", + "SSH_SFTP_READDIR_BOTTOM", + "SSH_SFTP_READDIR_DONE", + "SSH_SFTP_DOWNLOAD_INIT", + "SSH_SFTP_DOWNLOAD_STAT", + "SSH_SFTP_CLOSE", + "SSH_SFTP_SHUTDOWN", + "SSH_SCP_TRANS_INIT", + "SSH_SCP_UPLOAD_INIT", + "SSH_SCP_DOWNLOAD_INIT", + "SSH_SCP_DOWNLOAD", + "SSH_SCP_DONE", + "SSH_SCP_SEND_EOF", + "SSH_SCP_WAIT_EOF", + "SSH_SCP_WAIT_CLOSE", + "SSH_SCP_CHANNEL_FREE", + "SSH_SESSION_DISCONNECT", + "SSH_SESSION_FREE", + "QUIT" + }; + + /* a precaution to make sure the lists are in sync */ + DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST); + + if(sshc->state != nowstate) { + infof(conn->data, "wolfssh %p state change from %s to %s\n", + (void *)sshc, names[sshc->state], names[nowstate]); + } +#endif + + sshc->state = nowstate; +} + +static ssize_t wscp_send(struct connectdata *conn, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + ssize_t nwrite = 0; + (void)conn; + (void)sockindex; /* we only support SCP on the fixed known primary socket */ + (void)mem; + (void)len; + (void)err; + + return nwrite; +} + +static ssize_t wscp_recv(struct connectdata *conn, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + ssize_t nread = 0; + (void)conn; + (void)sockindex; /* we only support SCP on the fixed known primary socket */ + (void)mem; + (void)len; + (void)err; + + return nread; +} + +/* return number of sent bytes */ +static ssize_t wsftp_send(struct connectdata *conn, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + word32 offset[2]; + int rc; + (void)sockindex; + + offset[0] = (word32)sshc->offset&0xFFFFFFFF; + offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF; + + rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle, + sshc->handleSz, + &offset[0], + (byte *)mem, (word32)len); + + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + conn->waitfor = KEEP_RECV; + *err = CURLE_AGAIN; + return -1; + } + else if(rc == WS_WANT_WRITE) { + conn->waitfor = KEEP_SEND; + *err = CURLE_AGAIN; + return -1; + } + if(rc < 0) { + failf(conn->data, "wolfSSH_SFTP_SendWritePacket returned %d\n", rc); + return -1; + } + DEBUGASSERT(rc == (int)len); + infof(conn->data, "sent %zd bytes SFTP from offset %zd\n", + len, sshc->offset); + sshc->offset += len; + return (ssize_t)rc; +} + +/* + * Return number of received (decrypted) bytes + * or <0 on error + */ +static ssize_t wsftp_recv(struct connectdata *conn, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + int rc; + struct ssh_conn *sshc = &conn->proto.sshc; + word32 offset[2]; + (void)sockindex; + + offset[0] = (word32)sshc->offset&0xFFFFFFFF; + offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF; + + rc = wolfSSH_SFTP_SendReadPacket(sshc->ssh_session, sshc->handle, + sshc->handleSz, + &offset[0], + (byte *)mem, (word32)len); + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + conn->waitfor = KEEP_RECV; + *err = CURLE_AGAIN; + return -1; + } + else if(rc == WS_WANT_WRITE) { + conn->waitfor = KEEP_SEND; + *err = CURLE_AGAIN; + return -1; + } + + DEBUGASSERT(rc <= (int)len); + + if(rc < 0) { + failf(conn->data, "wolfSSH_SFTP_SendReadPacket returned %d\n", rc); + return -1; + } + sshc->offset += len; + + return (ssize_t)rc; +} + +/* + * SSH setup and connection + */ +static CURLcode wssh_setup_connection(struct connectdata *conn) +{ + struct SSHPROTO *ssh; + + conn->data->req.protop = ssh = calloc(1, sizeof(struct SSHPROTO)); + if(!ssh) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static Curl_recv wscp_recv, wsftp_recv; +static Curl_send wscp_send, wsftp_send; + +static int userauth(byte authtype, + WS_UserAuthData* authdata, + void *ctx) +{ + struct connectdata *conn = ctx; + DEBUGF(infof(conn->data, "wolfssh callback: type %s\n", + authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" : + "PUBLICCKEY")); + if(authtype == WOLFSSH_USERAUTH_PASSWORD) { + authdata->sf.password.password = (byte *)conn->passwd; + authdata->sf.password.passwordSz = (word32) strlen(conn->passwd); + } + + return 0; +} + +static CURLcode wssh_connect(struct connectdata *conn, bool *done) +{ + struct Curl_easy *data = conn->data; + struct ssh_conn *sshc; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int rc; + + /* initialize per-handle data if not already */ + if(!data->req.protop) + wssh_setup_connection(conn); + + /* We default to persistent connections. We set this already in this connect + function to make the re-use checks properly be able to check this bit. */ + connkeep(conn, "SSH default"); + + if(conn->handler->protocol & CURLPROTO_SCP) { + conn->recv[FIRSTSOCKET] = wscp_recv; + conn->send[FIRSTSOCKET] = wscp_send; + } + else { + conn->recv[FIRSTSOCKET] = wsftp_recv; + conn->send[FIRSTSOCKET] = wsftp_send; + } + sshc = &conn->proto.sshc; + sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); + if(!sshc->ctx) { + failf(data, "No wolfSSH context"); + goto error; + } + + sshc->ssh_session = wolfSSH_new(sshc->ctx); + if(sshc->ssh_session == NULL) { + failf(data, "No wolfSSH session"); + goto error; + } + + rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user); + if(rc != WS_SUCCESS) { + failf(data, "wolfSSH failed to set user name"); + goto error; + } + + /* set callback for authentication */ + wolfSSH_SetUserAuth(sshc->ctx, userauth); + wolfSSH_SetUserAuthCtx(sshc->ssh_session, conn); + + rc = wolfSSH_set_fd(sshc->ssh_session, (int)sock); + if(rc) { + failf(data, "wolfSSH failed to set socket"); + goto error; + } + +#if 0 + wolfSSH_Debugging_ON(); +#endif + + *done = TRUE; + if(conn->handler->protocol & CURLPROTO_SCP) + state(conn, SSH_INIT); + else + state(conn, SSH_SFTP_INIT); + + return wssh_multi_statemach(conn, done); + error: + wolfSSH_free(sshc->ssh_session); + wolfSSH_CTX_free(sshc->ctx); + return CURLE_FAILED_INIT; +} + +/* + * wssh_statemach_act() runs the SSH state machine as far as it can without + * blocking and without reaching the end. The data the pointer 'block' points + * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it + * wants to be called again when the socket is ready + */ + +static CURLcode wssh_statemach_act(struct connectdata *conn, bool *block) +{ + CURLcode result = CURLE_OK; + struct ssh_conn *sshc = &conn->proto.sshc; + struct Curl_easy *data = conn->data; + struct SSHPROTO *sftp_scp = data->req.protop; + WS_SFTPNAME *name; + int rc = 0; + *block = FALSE; /* we're not blocking by default */ + + do { + switch(sshc->state) { + case SSH_INIT: + state(conn, SSH_S_STARTUP); + /* FALLTHROUGH */ + case SSH_S_STARTUP: + rc = wolfSSH_connect(sshc->ssh_session); + if(rc != WS_SUCCESS) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc != WS_SUCCESS) { + state(conn, SSH_STOP); + return CURLE_SSH; + } + infof(data, "wolfssh connected!\n"); + state(conn, SSH_STOP); + break; + case SSH_STOP: + break; + + case SSH_SFTP_INIT: + rc = wolfSSH_SFTP_connect(sshc->ssh_session); + if(rc != WS_SUCCESS) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + infof(data, "wolfssh SFTP connected!\n"); + state(conn, SSH_SFTP_REALPATH); + } + else { + failf(data, "wolfssh SFTP connect error %d", rc); + return CURLE_SSH; + } + break; + case SSH_SFTP_REALPATH: + name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)"."); + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(name && (rc == WS_SUCCESS)) { + sshc->homedir = malloc(name->fSz + 1); + if(!sshc->homedir) { + sshc->actualcode = CURLE_OUT_OF_MEMORY; + } + else { + memcpy(sshc->homedir, name->fName, name->fSz); + sshc->homedir[name->fSz] = 0; + infof(data, "wolfssh SFTP realpath succeeded!\n"); + } + wolfSSH_SFTPNAME_list_free(name); + state(conn, SSH_STOP); + return CURLE_OK; + } + failf(data, "wolfssh SFTP realpath %d", rc); + return CURLE_SSH; + + case SSH_SFTP_QUOTE_INIT: + result = Curl_getworkingpath(conn, sshc->homedir, &sftp_scp->path); + if(result) { + sshc->actualcode = result; + state(conn, SSH_STOP); + break; + } + + if(data->set.quote) { + infof(data, "Sending quote commands\n"); + sshc->quote_item = data->set.quote; + state(conn, SSH_SFTP_QUOTE); + } + else { + state(conn, SSH_SFTP_GETINFO); + } + break; + case SSH_SFTP_GETINFO: + if(data->set.get_filetime) { + state(conn, SSH_SFTP_FILETIME); + } + else { + state(conn, SSH_SFTP_TRANS_INIT); + } + break; + case SSH_SFTP_TRANS_INIT: + if(data->set.upload) + state(conn, SSH_SFTP_UPLOAD_INIT); + else { + if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/') + state(conn, SSH_SFTP_READDIR_INIT); + else + state(conn, SSH_SFTP_DOWNLOAD_INIT); + } + break; + case SSH_SFTP_UPLOAD_INIT: { + word32 flags; + WS_SFTP_FILEATRB createattrs; + if(data->state.resume_from) { + WS_SFTP_FILEATRB attrs; + if(data->state.resume_from < 0) { + rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, + &attrs); + if(rc != WS_SUCCESS) + break; + + if(rc) { + data->state.resume_from = 0; + } + else { + curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0]; + if(size < 0) { + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + data->state.resume_from = size; + } + } + } + + if(data->set.ftp_append) + /* Try to open for append, but create if nonexisting */ + flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND; + else if(data->state.resume_from > 0) + /* If we have restart position then open for append */ + flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND; + else + /* Clear file before writing (normal behaviour) */ + flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC; + + memset(&createattrs, 0, sizeof(createattrs)); + createattrs.per = (word32)data->set.new_file_perms; + sshc->handleSz = sizeof(sshc->handle); + rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path, + flags, &createattrs, + sshc->handle, &sshc->handleSz); + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + infof(data, "wolfssh SFTP open succeeded!\n"); + } + else { + failf(data, "wolfssh SFTP upload open failed: %d", rc); + return CURLE_SSH; + } + state(conn, SSH_SFTP_DOWNLOAD_STAT); + + /* If we have a restart point then we need to seek to the correct + position. */ + if(data->state.resume_from > 0) { + /* Let's read off the proper amount of bytes from the input. */ + int seekerr = CURL_SEEKFUNC_OK; + if(conn->seek_func) { + Curl_set_in_callback(data, true); + seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, + SEEK_SET); + Curl_set_in_callback(data, false); + } + + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_FTP_COULDNT_USE_REST; + } + /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ + do { + size_t readthisamountnow = + (data->state.resume_from - passed > data->set.buffer_size) ? + (size_t)data->set.buffer_size : + curlx_sotouz(data->state.resume_from - passed); + + size_t actuallyread; + Curl_set_in_callback(data, true); + actuallyread = data->state.fread_func(data->state.buffer, 1, + readthisamountnow, + data->state.in); + Curl_set_in_callback(data, false); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + return CURLE_FTP_COULDNT_USE_REST; + } + } while(passed < data->state.resume_from); + } + + /* now, decrease the size of the read */ + if(data->state.infilesize > 0) { + data->state.infilesize -= data->state.resume_from; + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + + sshc->offset += data->state.resume_from; + } + if(data->state.infilesize > 0) { + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + /* upload data */ + Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); + + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->sockfd = conn->writesockfd; + + if(result) { + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + /* store this original bitmask setup to use later on if we can't + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh2 sftp send function will deal + with both accordingly */ + conn->cselect_bits = CURL_CSELECT_OUT; + + /* since we don't really wait for anything at this point, we want the + state machine to move on as soon as possible so we set a very short + timeout here */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + + state(conn, SSH_STOP); + } + break; + } + case SSH_SFTP_DOWNLOAD_INIT: + sshc->handleSz = sizeof(sshc->handle); + rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path, + WOLFSSH_FXF_READ, NULL, + sshc->handle, &sshc->handleSz); + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + infof(data, "wolfssh SFTP open succeeded!\n"); + state(conn, SSH_SFTP_DOWNLOAD_STAT); + return CURLE_OK; + } + + failf(data, "wolfssh SFTP open failed: %d", rc); + return CURLE_SSH; + + case SSH_SFTP_DOWNLOAD_STAT: { + WS_SFTP_FILEATRB attrs; + curl_off_t size; + + rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs); + if(rc == WS_FATAL_ERROR) + rc = wolfSSH_get_error(sshc->ssh_session); + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + infof(data, "wolfssh STAT succeeded!\n"); + } + else { + failf(data, "wolfssh SFTP open failed: %d", rc); + data->req.size = -1; + data->req.maxdownload = -1; + Curl_pgrsSetDownloadSize(data, -1); + return CURLE_SSH; + } + + size = ((curl_off_t)attrs.sz[1] <<32) | attrs.sz[0]; + + data->req.size = size; + data->req.maxdownload = size; + Curl_pgrsSetDownloadSize(data, size); + + infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes\n", size); + + /* We cannot seek with wolfSSH so resuming and range requests are not + possible */ + if(conn->data->state.use_range || data->state.resume_from) { + infof(data, "wolfSSH cannot do range/seek on SFTP\n"); + return CURLE_BAD_DOWNLOAD_RESUME; + } + + /* Setup the actual download */ + if(data->req.size == 0) { + /* no data to transfer */ + Curl_setup_transfer(data, -1, -1, FALSE, -1); + infof(data, "File already completely downloaded\n"); + state(conn, SSH_STOP); + break; + } + Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1); + + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->writesockfd = conn->sockfd; + + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh2 recv function will deal + with both accordingly */ + conn->cselect_bits = CURL_CSELECT_IN; + + if(result) { + /* this should never occur; the close state should be entered + at the time the error occurs */ + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + state(conn, SSH_STOP); + } + break; + } + case SSH_SFTP_CLOSE: + if(sshc->handleSz) + rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle, + sshc->handleSz); + else + rc = WS_SUCCESS; /* directory listing */ + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(rc == WS_SUCCESS) { + state(conn, SSH_STOP); + return CURLE_OK; + } + + failf(data, "wolfssh SFTP CLOSE failed: %d", rc); + return CURLE_SSH; + + case SSH_SFTP_READDIR_INIT: + Curl_pgrsSetDownloadSize(data, -1); + if(data->set.opt_no_body) { + state(conn, SSH_STOP); + break; + } + state(conn, SSH_SFTP_READDIR); + /* FALLTHROUGH */ + case SSH_SFTP_READDIR: + name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path); + if(!name) + rc = wolfSSH_get_error(sshc->ssh_session); + else + rc = WS_SUCCESS; + + if(rc == WS_WANT_READ) { + *block = TRUE; + conn->waitfor = KEEP_RECV; + return CURLE_OK; + } + else if(rc == WS_WANT_WRITE) { + *block = TRUE; + conn->waitfor = KEEP_SEND; + return CURLE_OK; + } + else if(name && (rc == WS_SUCCESS)) { + WS_SFTPNAME *origname = name; + result = CURLE_OK; + while(name) { + char *line = aprintf("%s\n", + data->set.ftp_list_only ? + name->fName : name->lName); + if(line == NULL) { + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + result = Curl_client_write(conn, CLIENTWRITE_BODY, + line, strlen(line)); + free(line); + if(result) { + sshc->actualcode = result; + break; + } + name = name->next; + } + wolfSSH_SFTPNAME_list_free(origname); + state(conn, SSH_STOP); + return result; + } + failf(data, "wolfssh SFTP ls failed: %d", rc); + return CURLE_SSH; + + case SSH_SFTP_SHUTDOWN: + Curl_safefree(sshc->homedir); + wolfSSH_free(sshc->ssh_session); + wolfSSH_CTX_free(sshc->ctx); + state(conn, SSH_STOP); + return CURLE_OK; + default: + break; + } + } while(!rc && (sshc->state != SSH_STOP)); + return result; +} + +/* called repeatedly until done from multi.c */ +static CURLcode wssh_multi_statemach(struct connectdata *conn, bool *done) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result = CURLE_OK; + bool block; /* we store the status and use that to provide a ssh_getsock() + implementation */ + do { + result = wssh_statemach_act(conn, &block); + *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; + /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then + try again */ + if(*done) { + DEBUGF(infof(conn->data, "wssh_statemach_act says DONE\n")); + } + } while(!result && !*done && !block); + + return result; +} + +static +CURLcode wscp_perform(struct connectdata *conn, + bool *connected, + bool *dophase_done) +{ + (void)conn; + (void)connected; + (void)dophase_done; + return CURLE_OK; +} + +static +CURLcode wsftp_perform(struct connectdata *conn, + bool *connected, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + + DEBUGF(infof(conn->data, "DO phase starts\n")); + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + state(conn, SSH_SFTP_QUOTE_INIT); + + /* run the state-machine */ + result = wssh_multi_statemach(conn, dophase_done); + + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; + + if(*dophase_done) { + DEBUGF(infof(conn->data, "DO phase is complete\n")); + } + + return result; +} + +/* + * The DO function is generic for both protocols. + */ +static CURLcode wssh_do(struct connectdata *conn, bool *done) +{ + CURLcode result; + bool connected = 0; + struct Curl_easy *data = conn->data; + struct ssh_conn *sshc = &conn->proto.sshc; + + *done = FALSE; /* default to false */ + data->req.size = -1; /* make sure this is unknown at this point */ + sshc->actualcode = CURLE_OK; /* reset error code */ + sshc->secondCreateDirs = 0; /* reset the create dir attempt state + variable */ + + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + + if(conn->handler->protocol & CURLPROTO_SCP) + result = wscp_perform(conn, &connected, done); + else + result = wsftp_perform(conn, &connected, done); + + return result; +} + +static CURLcode wssh_block_statemach(struct connectdata *conn, + bool disconnect) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result = CURLE_OK; + struct Curl_easy *data = conn->data; + + while((sshc->state != SSH_STOP) && !result) { + bool block; + timediff_t left = 1000; + struct curltime now = Curl_now(); + + result = wssh_statemach_act(conn, &block); + if(result) + break; + + if(!disconnect) { + if(Curl_pgrsUpdate(conn)) + return CURLE_ABORTED_BY_CALLBACK; + + result = Curl_speedcheck(data, now); + if(result) + break; + + left = Curl_timeleft(data, NULL, FALSE); + if(left < 0) { + failf(data, "Operation timed out"); + return CURLE_OPERATION_TIMEDOUT; + } + } + + if(!result) { + int dir = conn->waitfor; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + curl_socket_t fd_read = CURL_SOCKET_BAD; + curl_socket_t fd_write = CURL_SOCKET_BAD; + if(dir == KEEP_RECV) + fd_read = sock; + else if(dir == KEEP_SEND) + fd_write = sock; + + /* wait for the socket to become ready */ + (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, + left>1000?1000:left); /* ignore result */ + } + } + + return result; +} + +/* generic done function for both SCP and SFTP called from their specific + done functions */ +static CURLcode wssh_done(struct connectdata *conn, CURLcode status) +{ + CURLcode result = CURLE_OK; + struct SSHPROTO *sftp_scp = conn->data->req.protop; + + if(!status) { + /* run the state-machine */ + result = wssh_block_statemach(conn, FALSE); + } + else + result = status; + + if(sftp_scp) + Curl_safefree(sftp_scp->path); + if(Curl_pgrsDone(conn)) + return CURLE_ABORTED_BY_CALLBACK; + + conn->data->req.keepon = 0; /* clear all bits */ + return result; +} + +#if 0 +static CURLcode wscp_done(struct connectdata *conn, + CURLcode code, bool premature) +{ + CURLcode result = CURLE_OK; + (void)conn; + (void)code; + (void)premature; + + return result; +} + +static CURLcode wscp_doing(struct connectdata *conn, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + (void)conn; + (void)dophase_done; + + return result; +} + +static CURLcode wscp_disconnect(struct connectdata *conn, bool dead_connection) +{ + CURLcode result = CURLE_OK; + (void)conn; + (void)dead_connection; + + return result; +} +#endif + +static CURLcode wsftp_done(struct connectdata *conn, + CURLcode code, bool premature) +{ + (void)premature; + state(conn, SSH_SFTP_CLOSE); + + return wssh_done(conn, code); +} + +static CURLcode wsftp_doing(struct connectdata *conn, + bool *dophase_done) +{ + CURLcode result = wssh_multi_statemach(conn, dophase_done); + + if(*dophase_done) { + DEBUGF(infof(conn->data, "DO phase is complete\n")); + } + return result; +} + +static CURLcode wsftp_disconnect(struct connectdata *conn, bool dead) +{ + CURLcode result = CURLE_OK; + (void)dead; + + DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n")); + + if(conn->proto.sshc.ssh_session) { + /* only if there's a session still around to use! */ + state(conn, SSH_SFTP_SHUTDOWN); + result = wssh_block_statemach(conn, TRUE); + } + + DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n")); + return result; +} + +static int wssh_getsock(struct connectdata *conn, + curl_socket_t *sock) +{ + return wssh_perform_getsock(conn, sock); +} + +static int wssh_perform_getsock(const struct connectdata *conn, + curl_socket_t *sock) +{ + int bitmap = GETSOCK_BLANK; + int dir = conn->waitfor; + sock[0] = conn->sock[FIRSTSOCKET]; + + if(dir == KEEP_RECV) + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + else if(dir == KEEP_SEND) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} + +size_t Curl_ssh_version(char *buffer, size_t buflen) +{ + return msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING); +} + +CURLcode Curl_ssh_init(void) +{ + if(WS_SUCCESS != wolfSSH_Init()) { + DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n")); + return CURLE_FAILED_INIT; + } + + return CURLE_OK; +} +void Curl_ssh_cleanup(void) +{ +} + +#endif /* USE_WOLFSSH */ diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.h b/Utilities/cmcurl/lib/vssh/wolfssh.h new file mode 100644 index 00000000000..a9b9a3b0917 --- /dev/null +++ b/Utilities/cmcurl/lib/vssh/wolfssh.h @@ -0,0 +1,27 @@ +#ifndef HEADER_CURL_WOLFSSH_H +#define HEADER_CURL_WOLFSSH_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2019 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ + +extern const struct Curl_handler Curl_handler_sftp; + +#endif /* HEADER_CURL_WOLFSSH_H */ diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c new file mode 100644 index 00000000000..628e16a124a --- /dev/null +++ b/Utilities/cmcurl/lib/vtls/bearssl.c @@ -0,0 +1,870 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2019 - 2020, Michael Forney, + * + * 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.haxx.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. + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_BEARSSL + +#include + +#include "bearssl.h" +#include "urldata.h" +#include "sendf.h" +#include "inet_pton.h" +#include "vtls.h" +#include "connect.h" +#include "select.h" +#include "multiif.h" +#include "curl_printf.h" +#include "curl_memory.h" + +struct x509_context { + const br_x509_class *vtable; + br_x509_minimal_context minimal; + bool verifyhost; + bool verifypeer; +}; + +struct ssl_backend_data { + br_ssl_client_context ctx; + struct x509_context x509; + unsigned char buf[BR_SSL_BUFSIZE_BIDI]; + br_x509_trust_anchor *anchors; + size_t anchors_len; + const char *protocols[2]; + /* SSL client context is active */ + bool active; + /* size of pending write, yet to be flushed */ + size_t pending_write; +}; + +struct cafile_parser { + CURLcode err; + bool in_cert; + br_x509_decoder_context xc; + /* array of trust anchors loaded from CAfile */ + br_x509_trust_anchor *anchors; + size_t anchors_len; + /* buffer for DN data */ + unsigned char dn[1024]; + size_t dn_len; +}; + +static void append_dn(void *ctx, const void *buf, size_t len) +{ + struct cafile_parser *ca = ctx; + + if(ca->err != CURLE_OK || !ca->in_cert) + return; + if(sizeof(ca->dn) - ca->dn_len < len) { + ca->err = CURLE_FAILED_INIT; + return; + } + memcpy(ca->dn + ca->dn_len, buf, len); + ca->dn_len += len; +} + +static void x509_push(void *ctx, const void *buf, size_t len) +{ + struct cafile_parser *ca = ctx; + + if(ca->in_cert) + br_x509_decoder_push(&ca->xc, buf, len); +} + +static CURLcode load_cafile(const char *path, br_x509_trust_anchor **anchors, + size_t *anchors_len) +{ + struct cafile_parser ca; + br_pem_decoder_context pc; + br_x509_trust_anchor *ta; + size_t ta_size; + br_x509_trust_anchor *new_anchors; + size_t new_anchors_len; + br_x509_pkey *pkey; + FILE *fp; + unsigned char buf[BUFSIZ], *p; + const char *name; + size_t n, i, pushed; + + fp = fopen(path, "rb"); + if(!fp) + return CURLE_SSL_CACERT_BADFILE; + + ca.err = CURLE_OK; + ca.in_cert = FALSE; + ca.anchors = NULL; + ca.anchors_len = 0; + br_pem_decoder_init(&pc); + br_pem_decoder_setdest(&pc, x509_push, &ca); + for(;;) { + n = fread(buf, 1, sizeof(buf), fp); + if(n == 0) + break; + p = buf; + while(n) { + pushed = br_pem_decoder_push(&pc, p, n); + if(ca.err) + goto fail; + p += pushed; + n -= pushed; + + switch(br_pem_decoder_event(&pc)) { + case 0: + break; + case BR_PEM_BEGIN_OBJ: + name = br_pem_decoder_name(&pc); + if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE")) + break; + br_x509_decoder_init(&ca.xc, append_dn, &ca); + if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) { + ca.err = CURLE_OUT_OF_MEMORY; + goto fail; + } + new_anchors_len = ca.anchors_len + 1; + new_anchors = realloc(ca.anchors, + new_anchors_len * sizeof(ca.anchors[0])); + if(!new_anchors) { + ca.err = CURLE_OUT_OF_MEMORY; + goto fail; + } + ca.anchors = new_anchors; + ca.anchors_len = new_anchors_len; + ca.in_cert = TRUE; + ca.dn_len = 0; + ta = &ca.anchors[ca.anchors_len - 1]; + ta->dn.data = NULL; + break; + case BR_PEM_END_OBJ: + if(!ca.in_cert) + break; + ca.in_cert = FALSE; + if(br_x509_decoder_last_error(&ca.xc)) { + ca.err = CURLE_SSL_CACERT_BADFILE; + goto fail; + } + ta->flags = 0; + if(br_x509_decoder_isCA(&ca.xc)) + ta->flags |= BR_X509_TA_CA; + pkey = br_x509_decoder_get_pkey(&ca.xc); + if(!pkey) { + ca.err = CURLE_SSL_CACERT_BADFILE; + goto fail; + } + ta->pkey = *pkey; + + /* calculate space needed for trust anchor data */ + ta_size = ca.dn_len; + switch(pkey->key_type) { + case BR_KEYTYPE_RSA: + ta_size += pkey->key.rsa.nlen + pkey->key.rsa.elen; + break; + case BR_KEYTYPE_EC: + ta_size += pkey->key.ec.qlen; + break; + default: + ca.err = CURLE_FAILED_INIT; + goto fail; + } + + /* fill in trust anchor DN and public key data */ + ta->dn.data = malloc(ta_size); + if(!ta->dn.data) { + ca.err = CURLE_OUT_OF_MEMORY; + goto fail; + } + memcpy(ta->dn.data, ca.dn, ca.dn_len); + ta->dn.len = ca.dn_len; + switch(pkey->key_type) { + case BR_KEYTYPE_RSA: + ta->pkey.key.rsa.n = ta->dn.data + ta->dn.len; + memcpy(ta->pkey.key.rsa.n, pkey->key.rsa.n, pkey->key.rsa.nlen); + ta->pkey.key.rsa.e = ta->pkey.key.rsa.n + ta->pkey.key.rsa.nlen; + memcpy(ta->pkey.key.rsa.e, pkey->key.rsa.e, pkey->key.rsa.elen); + break; + case BR_KEYTYPE_EC: + ta->pkey.key.ec.q = ta->dn.data + ta->dn.len; + memcpy(ta->pkey.key.ec.q, pkey->key.ec.q, pkey->key.ec.qlen); + break; + } + break; + default: + ca.err = CURLE_SSL_CACERT_BADFILE; + goto fail; + } + } + } + if(ferror(fp)) + ca.err = CURLE_READ_ERROR; + +fail: + fclose(fp); + if(ca.err == CURLE_OK) { + *anchors = ca.anchors; + *anchors_len = ca.anchors_len; + } + else { + for(i = 0; i < ca.anchors_len; ++i) + free(ca.anchors[i].dn.data); + free(ca.anchors); + } + + return ca.err; +} + +static void x509_start_chain(const br_x509_class **ctx, + const char *server_name) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + if(!x509->verifyhost) + server_name = NULL; + x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name); +} + +static void x509_start_cert(const br_x509_class **ctx, uint32_t length) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + x509->minimal.vtable->start_cert(&x509->minimal.vtable, length); +} + +static void x509_append(const br_x509_class **ctx, const unsigned char *buf, + size_t len) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + x509->minimal.vtable->append(&x509->minimal.vtable, buf, len); +} + +static void x509_end_cert(const br_x509_class **ctx) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + x509->minimal.vtable->end_cert(&x509->minimal.vtable); +} + +static unsigned x509_end_chain(const br_x509_class **ctx) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + unsigned err; + + err = x509->minimal.vtable->end_chain(&x509->minimal.vtable); + if(err && !x509->verifypeer) { + /* ignore any X.509 errors */ + err = BR_ERR_OK; + } + + return err; +} + +static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx, + unsigned *usages) +{ + struct x509_context *x509 = (struct x509_context *)ctx; + + return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages); +} + +static const br_x509_class x509_vtable = { + sizeof(struct x509_context), + x509_start_chain, + x509_start_cert, + x509_append, + x509_end_cert, + x509_end_chain, + x509_get_pkey +}; + +static CURLcode bearssl_connect_step1(struct connectdata *conn, int sockindex) +{ + struct Curl_easy *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile); + const char *hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : + conn->host.name; + const bool verifypeer = SSL_CONN_CONFIG(verifypeer); + const bool verifyhost = SSL_CONN_CONFIG(verifyhost); + CURLcode ret; + unsigned version_min, version_max; +#ifdef ENABLE_IPV6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + + switch(SSL_CONN_CONFIG(version)) { + case CURL_SSLVERSION_SSLv2: + failf(data, "BearSSL does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; + case CURL_SSLVERSION_SSLv3: + failf(data, "BearSSL does not support SSLv3"); + return CURLE_SSL_CONNECT_ERROR; + case CURL_SSLVERSION_TLSv1_0: + version_min = BR_TLS10; + version_max = BR_TLS10; + break; + case CURL_SSLVERSION_TLSv1_1: + version_min = BR_TLS11; + version_max = BR_TLS11; + break; + case CURL_SSLVERSION_TLSv1_2: + version_min = BR_TLS12; + version_max = BR_TLS12; + break; + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + version_min = BR_TLS10; + version_max = BR_TLS12; + break; + default: + failf(data, "BearSSL: unknown CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(ssl_cafile) { + ret = load_cafile(ssl_cafile, &backend->anchors, &backend->anchors_len); + if(ret != CURLE_OK) { + if(verifypeer) { + failf(data, "error setting certificate verify locations:\n" + " CAfile: %s\n", ssl_cafile); + return ret; + } + infof(data, "error setting certificate verify locations," + " continuing anyway:\n"); + } + } + + /* initialize SSL context */ + br_ssl_client_init_full(&backend->ctx, &backend->x509.minimal, + backend->anchors, backend->anchors_len); + br_ssl_engine_set_versions(&backend->ctx.eng, version_min, version_max); + br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf, + sizeof(backend->buf), 1); + + /* initialize X.509 context */ + backend->x509.vtable = &x509_vtable; + backend->x509.verifypeer = verifypeer; + backend->x509.verifyhost = verifyhost; + br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); + + if(SSL_SET_OPTION(primary.sessionid)) { + void *session; + + Curl_ssl_sessionid_lock(conn); + if(!Curl_ssl_getsessionid(conn, &session, NULL, sockindex)) { + br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); + infof(data, "BearSSL: re-using session ID\n"); + } + Curl_ssl_sessionid_unlock(conn); + } + + if(conn->bits.tls_enable_alpn) { + int cur = 0; + + /* NOTE: when adding more protocols here, increase the size of the + * protocols array in `struct ssl_backend_data`. + */ + +#ifdef USE_NGHTTP2 + if(data->set.httpversion >= CURL_HTTP_VERSION_2 && + (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)) { + backend->protocols[cur++] = NGHTTP2_PROTO_VERSION_ID; + infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); + } +#endif + + backend->protocols[cur++] = ALPN_HTTP_1_1; + infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1); + + br_ssl_engine_set_protocol_names(&backend->ctx.eng, + backend->protocols, cur); + } + + if((1 == Curl_inet_pton(AF_INET, hostname, &addr)) +#ifdef ENABLE_IPV6 + || (1 == Curl_inet_pton(AF_INET6, hostname, &addr)) +#endif + ) { + if(verifyhost) { + failf(data, "BearSSL: " + "host verification of IP address is not supported"); + return CURLE_PEER_FAILED_VERIFICATION; + } + hostname = NULL; + } + + if(!br_ssl_client_reset(&backend->ctx, hostname, 0)) + return CURLE_FAILED_INIT; + backend->active = TRUE; + + connssl->connecting_state = ssl_connect_2; + + return CURLE_OK; +} + +static CURLcode bearssl_run_until(struct connectdata *conn, int sockindex, + unsigned target) +{ + struct Curl_easy *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + curl_socket_t sockfd = conn->sock[sockindex]; + unsigned state; + unsigned char *buf; + size_t len; + ssize_t ret; + int err; + + for(;;) { + state = br_ssl_engine_current_state(&backend->ctx.eng); + if(state & BR_SSL_CLOSED) { + err = br_ssl_engine_last_error(&backend->ctx.eng); + switch(err) { + case BR_ERR_OK: + /* TLS close notify */ + if(connssl->state != ssl_connection_complete) { + failf(data, "SSL: connection closed during handshake"); + return CURLE_SSL_CONNECT_ERROR; + } + return CURLE_OK; + case BR_ERR_X509_EXPIRED: + failf(data, "SSL: X.509 verification: " + "certificate is expired or not yet valid"); + return CURLE_PEER_FAILED_VERIFICATION; + case BR_ERR_X509_BAD_SERVER_NAME: + failf(data, "SSL: X.509 verification: " + "expected server name was not found in the chain"); + return CURLE_PEER_FAILED_VERIFICATION; + case BR_ERR_X509_NOT_TRUSTED: + failf(data, "SSL: X.509 verification: " + "chain could not be linked to a trust anchor"); + return CURLE_PEER_FAILED_VERIFICATION; + } + /* X.509 errors are documented to have the range 32..63 */ + if(err >= 32 && err < 64) + return CURLE_PEER_FAILED_VERIFICATION; + return CURLE_SSL_CONNECT_ERROR; + } + if(state & target) + return CURLE_OK; + if(state & BR_SSL_SENDREC) { + buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); + ret = swrite(sockfd, buf, len); + if(ret == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + if(connssl->state != ssl_connection_complete) + connssl->connecting_state = ssl_connect_2_writing; + return CURLE_AGAIN; + } + return CURLE_WRITE_ERROR; + } + br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret); + } + else if(state & BR_SSL_RECVREC) { + buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); + ret = sread(sockfd, buf, len); + if(ret == 0) { + failf(data, "SSL: EOF without close notify"); + return CURLE_READ_ERROR; + } + if(ret == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + if(connssl->state != ssl_connection_complete) + connssl->connecting_state = ssl_connect_2_reading; + return CURLE_AGAIN; + } + return CURLE_READ_ERROR; + } + br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret); + } + } +} + +static CURLcode bearssl_connect_step2(struct connectdata *conn, int sockindex) +{ + struct Curl_easy *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + CURLcode ret; + + ret = bearssl_run_until(conn, sockindex, BR_SSL_SENDAPP | BR_SSL_RECVAPP); + if(ret == CURLE_AGAIN) + return CURLE_OK; + if(ret == CURLE_OK) { + if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { + failf(data, "SSL: connection closed during handshake"); + return CURLE_SSL_CONNECT_ERROR; + } + connssl->connecting_state = ssl_connect_3; + } + return ret; +} + +static CURLcode bearssl_connect_step3(struct connectdata *conn, int sockindex) +{ + struct Curl_easy *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + CURLcode ret; + + DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + + if(conn->bits.tls_enable_alpn) { + const char *protocol; + + protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); + if(protocol) { + infof(data, "ALPN, server accepted to use %s\n", protocol); + +#ifdef USE_NGHTTP2 + if(!strcmp(protocol, NGHTTP2_PROTO_VERSION_ID)) + conn->negnpn = CURL_HTTP_VERSION_2; + else +#endif + if(!strcmp(protocol, ALPN_HTTP_1_1)) + conn->negnpn = CURL_HTTP_VERSION_1_1; + else + infof(data, "ALPN, unrecognized protocol %s\n", protocol); + Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + } + else + infof(data, "ALPN, server did not agree to a protocol\n"); + } + + if(SSL_SET_OPTION(primary.sessionid)) { + bool incache; + void *oldsession; + br_ssl_session_parameters *session; + + session = malloc(sizeof(*session)); + if(!session) + return CURLE_OUT_OF_MEMORY; + br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); + Curl_ssl_sessionid_lock(conn); + incache = !(Curl_ssl_getsessionid(conn, &oldsession, NULL, sockindex)); + if(incache) + Curl_ssl_delsessionid(conn, oldsession); + ret = Curl_ssl_addsessionid(conn, session, 0, sockindex); + Curl_ssl_sessionid_unlock(conn); + if(ret) { + free(session); + return CURLE_OUT_OF_MEMORY; + } + } + + connssl->connecting_state = ssl_connect_done; + + return CURLE_OK; +} + +static ssize_t bearssl_send(struct connectdata *conn, int sockindex, + const void *buf, size_t len, CURLcode *err) +{ + struct Curl_easy *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + unsigned char *app; + size_t applen; + + for(;;) { + *err = bearssl_run_until(conn, sockindex, BR_SSL_SENDAPP); + if (*err != CURLE_OK) + return -1; + app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen); + if(!app) { + failf(data, "SSL: connection closed during write"); + *err = CURLE_SEND_ERROR; + return -1; + } + if(backend->pending_write) { + applen = backend->pending_write; + backend->pending_write = 0; + return applen; + } + if(applen > len) + applen = len; + memcpy(app, buf, applen); + br_ssl_engine_sendapp_ack(&backend->ctx.eng, applen); + br_ssl_engine_flush(&backend->ctx.eng, 0); + backend->pending_write = applen; + } +} + +static ssize_t bearssl_recv(struct connectdata *conn, int sockindex, + char *buf, size_t len, CURLcode *err) +{ + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + unsigned char *app; + size_t applen; + + *err = bearssl_run_until(conn, sockindex, BR_SSL_RECVAPP); + if(*err != CURLE_OK) + return -1; + app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen); + if(!app) + return 0; + if(applen > len) + applen = len; + memcpy(buf, app, applen); + br_ssl_engine_recvapp_ack(&backend->ctx.eng, applen); + + return applen; +} + +static CURLcode bearssl_connect_common(struct connectdata *conn, + int sockindex, + bool nonblocking, + bool *done) +{ + CURLcode ret; + struct Curl_easy *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + curl_socket_t sockfd = conn->sock[sockindex]; + timediff_t timeout_ms; + int what; + + /* check if the connection has already been established */ + if(ssl_connection_complete == connssl->state) { + *done = TRUE; + return CURLE_OK; + } + + if(ssl_connect_1 == connssl->connecting_state) { + ret = bearssl_connect_step1(conn, sockindex); + if(ret) + return ret; + } + + while(ssl_connect_2 == connssl->connecting_state || + ssl_connect_2_reading == connssl->connecting_state || + ssl_connect_2_writing == connssl->connecting_state) { + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + /* if ssl is expecting something, check if it's available. */ + if(ssl_connect_2_reading == connssl->connecting_state || + ssl_connect_2_writing == connssl->connecting_state) { + + curl_socket_t writefd = ssl_connect_2_writing == + connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = ssl_connect_2_reading == + connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + nonblocking?0:timeout_ms); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + return CURLE_SSL_CONNECT_ERROR; + } + else if(0 == what) { + if(nonblocking) { + *done = FALSE; + return CURLE_OK; + } + else { + /* timeout */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + } + /* socket is readable or writable */ + } + + /* Run transaction, and return to the caller if it failed or if this + * connection is done nonblocking and this loop would execute again. This + * permits the owner of a multi handle to abort a connection attempt + * before step2 has completed while ensuring that a client using select() + * or epoll() will always have a valid fdset to wait on. + */ + ret = bearssl_connect_step2(conn, sockindex); + if(ret || (nonblocking && + (ssl_connect_2 == connssl->connecting_state || + ssl_connect_2_reading == connssl->connecting_state || + ssl_connect_2_writing == connssl->connecting_state))) + return ret; + } + + if(ssl_connect_3 == connssl->connecting_state) { + ret = bearssl_connect_step3(conn, sockindex); + if(ret) + return ret; + } + + if(ssl_connect_done == connssl->connecting_state) { + connssl->state = ssl_connection_complete; + conn->recv[sockindex] = bearssl_recv; + conn->send[sockindex] = bearssl_send; + *done = TRUE; + } + else + *done = FALSE; + + /* Reset our connect state machine */ + connssl->connecting_state = ssl_connect_1; + + return CURLE_OK; +} + +static size_t Curl_bearssl_version(char *buffer, size_t size) +{ + return msnprintf(buffer, size, "BearSSL"); +} + +static bool Curl_bearssl_data_pending(const struct connectdata *conn, + int connindex) +{ + const struct ssl_connect_data *connssl = &conn->ssl[connindex]; + struct ssl_backend_data *backend = connssl->backend; + return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; +} + +static CURLcode Curl_bearssl_random(struct Curl_easy *data UNUSED_PARAM, + unsigned char *entropy, size_t length) +{ + static br_hmac_drbg_context ctx; + static bool seeded = FALSE; + + if(!seeded) { + br_prng_seeder seeder; + + br_hmac_drbg_init(&ctx, &br_sha256_vtable, NULL, 0); + seeder = br_prng_seeder_system(NULL); + if(!seeder || !seeder(&ctx.vtable)) + return CURLE_FAILED_INIT; + seeded = TRUE; + } + br_hmac_drbg_generate(&ctx, entropy, length); + + return CURLE_OK; +} + +static CURLcode Curl_bearssl_connect(struct connectdata *conn, int sockindex) +{ + CURLcode ret; + bool done = FALSE; + + ret = bearssl_connect_common(conn, sockindex, FALSE, &done); + if(ret) + return ret; + + DEBUGASSERT(done); + + return CURLE_OK; +} + +static CURLcode Curl_bearssl_connect_nonblocking(struct connectdata *conn, + int sockindex, bool *done) +{ + return bearssl_connect_common(conn, sockindex, TRUE, done); +} + +static void *Curl_bearssl_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + struct ssl_backend_data *backend = connssl->backend; + return &backend->ctx; +} + +static void Curl_bearssl_close(struct connectdata *conn, int sockindex) +{ + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + size_t i; + + if(backend->active) { + br_ssl_engine_close(&backend->ctx.eng); + (void)bearssl_run_until(conn, sockindex, BR_SSL_CLOSED); + } + for(i = 0; i < backend->anchors_len; ++i) + free(backend->anchors[i].dn.data); + free(backend->anchors); +} + +static void Curl_bearssl_session_free(void *ptr) +{ + free(ptr); +} + +static CURLcode Curl_bearssl_md5sum(unsigned char *input, + size_t inputlen, + unsigned char *md5sum, + size_t md5len UNUSED_PARAM) +{ + br_md5_context ctx; + + br_md5_init(&ctx); + br_md5_update(&ctx, input, inputlen); + br_md5_out(&ctx, md5sum); + return CURLE_OK; +} + +static CURLcode Curl_bearssl_sha256sum(const unsigned char *input, + size_t inputlen, + unsigned char *sha256sum, + size_t sha256len UNUSED_PARAM) +{ + br_sha256_context ctx; + + br_sha256_init(&ctx); + br_sha256_update(&ctx, input, inputlen); + br_sha256_out(&ctx, sha256sum); + return CURLE_OK; +} + +const struct Curl_ssl Curl_ssl_bearssl = { + { CURLSSLBACKEND_BEARSSL, "bearssl" }, + 0, + sizeof(struct ssl_backend_data), + + Curl_none_init, + Curl_none_cleanup, + Curl_bearssl_version, + Curl_none_check_cxn, + Curl_none_shutdown, + Curl_bearssl_data_pending, + Curl_bearssl_random, + Curl_none_cert_status_request, + Curl_bearssl_connect, + Curl_bearssl_connect_nonblocking, + Curl_bearssl_get_internals, + Curl_bearssl_close, + Curl_none_close_all, + Curl_bearssl_session_free, + Curl_none_set_engine, + Curl_none_set_engine_default, + Curl_none_engines_list, + Curl_none_false_start, + Curl_bearssl_md5sum, + Curl_bearssl_sha256sum +}; + +#endif /* USE_BEARSSL */ diff --git a/Utilities/cmcurl/lib/vtls/polarssl.h b/Utilities/cmcurl/lib/vtls/bearssl.h similarity index 75% rename from Utilities/cmcurl/lib/vtls/polarssl.h rename to Utilities/cmcurl/lib/vtls/bearssl.h index 23c3636ee6a..5f94922b928 100644 --- a/Utilities/cmcurl/lib/vtls/polarssl.h +++ b/Utilities/cmcurl/lib/vtls/bearssl.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_POLARSSL_H -#define HEADER_CURL_POLARSSL_H +#ifndef HEADER_CURL_BEARSSL_H +#define HEADER_CURL_BEARSSL_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,8 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2016, Daniel Stenberg, , et al. - * Copyright (C) 2010, Hoi-Ho Chan, + * Copyright (C) 2019, Michael Forney, * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,11 +21,12 @@ * KIND, either express or implied. * ***************************************************************************/ + #include "curl_setup.h" -#ifdef USE_POLARSSL +#ifdef USE_BEARSSL -extern const struct Curl_ssl Curl_ssl_polarssl; +extern const struct Curl_ssl Curl_ssl_bearssl; -#endif /* USE_POLARSSL */ -#endif /* HEADER_CURL_POLARSSL_H */ +#endif /* USE_BEARSSL */ +#endif /* HEADER_CURL_BEARSSL_H */ diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c index b93ff5d4f4f..0538e4a4639 100644 --- a/Utilities/cmcurl/lib/vtls/gskit.c +++ b/Utilities/cmcurl/lib/vtls/gskit.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,8 @@ #include #include +#undef HAVE_SOCKETPAIR /* because the native one isn't good enough */ +#include "socketpair.h" /* Some symbols are undefined/unsupported on OS400 versions < V7R1. */ #ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST @@ -106,13 +108,13 @@ struct ssl_backend_data { #define BACKEND connssl->backend /* Supported ciphers. */ -typedef struct { +struct gskit_cipher { const char *name; /* Cipher name. */ const char *gsktoken; /* Corresponding token for GSKit String. */ unsigned int versions; /* SSL version flags. */ -} gskit_cipher; +}; -static const gskit_cipher ciphertable[] = { +static const struct gskit_cipher ciphertable[] = { { "null-md5", "01", CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, @@ -305,7 +307,7 @@ static CURLcode set_ciphers(struct connectdata *conn, struct Curl_easy *data = conn->data; const char *cipherlist = SSL_CONN_CONFIG(cipher_list); const char *clp; - const gskit_cipher *ctp; + const struct gskit_cipher *ctp; int i; int l; bool unsupported; @@ -511,100 +513,6 @@ static void close_async_handshake(struct ssl_connect_data *connssl) BACKEND->iocport = -1; } -/* SSL over SSL - * Problems: - * 1) GSKit can only perform SSL on an AF_INET or AF_INET6 stream socket. To - * pipe an SSL stream into another, it is therefore needed to have a pair - * of such communicating sockets and handle the pipelining explicitly. - * 2) OS/400 socketpair() is only implemented for domain AF_UNIX, thus cannot - * be used to produce the pipeline. - * The solution is to simulate socketpair() for AF_INET with low-level API - * listen(), bind() and connect(). - */ - -static int -inetsocketpair(int sv[2]) -{ - int lfd; /* Listening socket. */ - int sfd; /* Server socket. */ - int cfd; /* Client socket. */ - int len; - struct sockaddr_in addr1; - struct sockaddr_in addr2; - - /* Create listening socket on a local dynamic port. */ - lfd = socket(AF_INET, SOCK_STREAM, 0); - if(lfd < 0) - return -1; - memset((char *) &addr1, 0, sizeof(addr1)); - addr1.sin_family = AF_INET; - addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr1.sin_port = 0; - if(bind(lfd, (struct sockaddr *) &addr1, sizeof(addr1)) || - listen(lfd, 2) < 0) { - close(lfd); - return -1; - } - - /* Get the allocated port. */ - len = sizeof(addr1); - if(getsockname(lfd, (struct sockaddr *) &addr1, &len) < 0) { - close(lfd); - return -1; - } - - /* Create the client socket. */ - cfd = socket(AF_INET, SOCK_STREAM, 0); - if(cfd < 0) { - close(lfd); - return -1; - } - - /* Request unblocking connection to the listening socket. */ - curlx_nonblock(cfd, TRUE); - if(connect(cfd, (struct sockaddr *) &addr1, sizeof(addr1)) < 0 && - errno != EINPROGRESS) { - close(lfd); - close(cfd); - return -1; - } - - /* Get the client dynamic port for intrusion check below. */ - len = sizeof(addr2); - if(getsockname(cfd, (struct sockaddr *) &addr2, &len) < 0) { - close(lfd); - close(cfd); - return -1; - } - - /* Accept the incoming connection and get the server socket. */ - curlx_nonblock(lfd, TRUE); - for(;;) { - len = sizeof(addr1); - sfd = accept(lfd, (struct sockaddr *) &addr1, &len); - if(sfd < 0) { - close(lfd); - close(cfd); - return -1; - } - - /* Check for possible intrusion from an external process. */ - if(addr1.sin_addr.s_addr == addr2.sin_addr.s_addr && - addr1.sin_port == addr2.sin_port) - break; - - /* Intrusion: reject incoming connection. */ - close(sfd); - } - - /* Done, return sockets and succeed. */ - close(lfd); - curlx_nonblock(cfd, FALSE); - sv[0] = cfd; - sv[1] = sfd; - return 0; -} - static int pipe_ssloverssl(struct connectdata *conn, int sockindex, int directions) { @@ -616,7 +524,6 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex, int m; int i; int ret = 0; - struct timeval tv = {0, 0}; char buf[CURL_MAX_WRITE_SIZE]; if(!connssl->use || !connproxyssl->use) @@ -636,7 +543,7 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex, if(n < conn->sock[sockindex]) n = conn->sock[sockindex]; } - i = select(n + 1, &fds_read, &fds_write, NULL, &tv); + i = Curl_select(n + 1, &fds_read, &fds_write, NULL, 0); if(i < 0) return -1; /* Select error. */ @@ -855,7 +762,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex) /* Establish a pipelining socket pair for SSL over SSL. */ if(conn->proxy_ssl[sockindex].use) { - if(inetsocketpair(sockpair)) + if(Curl_socketpair(0, 0, 0, sockpair)) return CURLE_SSL_CONNECT_ERROR; BACKEND->localfd = sockpair[0]; BACKEND->remotefd = sockpair[1]; @@ -912,7 +819,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex) if(!result) { /* Compute the handshake timeout. Since GSKit granularity is 1 second, we round up the required value. */ - long timeout = Curl_timeleft(data, NULL, TRUE); + timediff_t timeout = Curl_timeleft(data, NULL, TRUE); if(timeout < 0) result = CURLE_OPERATION_TIMEDOUT; else @@ -1025,7 +932,7 @@ static CURLcode gskit_connect_step2(struct connectdata *conn, int sockindex, /* Poll or wait for end of SSL asynchronous handshake. */ for(;;) { - long timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); + timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) timeout_ms = 0; stmv.tv_sec = timeout_ms / 1000; @@ -1157,7 +1064,7 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex, { struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - long timeout_ms; + timediff_t timeout_ms; CURLcode result = CURLE_OK; *done = connssl->state == ssl_connection_complete; diff --git a/Utilities/cmcurl/lib/vtls/gskit.h b/Utilities/cmcurl/lib/vtls/gskit.h index 466ee4d9de4..b06b5e17d3e 100644 --- a/Utilities/cmcurl/lib/vtls/gskit.h +++ b/Utilities/cmcurl/lib/vtls/gskit.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index 8693cdce3fd..9b4c3659acc 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -72,36 +72,11 @@ static void tls_log_func(int level, const char *str) #endif static bool gtls_inited = FALSE; -#if defined(GNUTLS_VERSION_NUMBER) -# if (GNUTLS_VERSION_NUMBER >= 0x020c00) -# undef gnutls_transport_set_lowat -# define gnutls_transport_set_lowat(A,B) Curl_nop_stmt -# define USE_GNUTLS_PRIORITY_SET_DIRECT 1 -# endif -# if (GNUTLS_VERSION_NUMBER >= 0x020c03) -# define GNUTLS_MAPS_WINSOCK_ERRORS 1 -# endif - -# if HAVE_GNUTLS_ALPN_SET_PROTOCOLS -# define HAS_ALPN -# endif - -# if HAVE_GNUTLS_OCSP_REQ_INIT -# define HAS_OCSP -# endif - -# if (GNUTLS_VERSION_NUMBER >= 0x030306) -# define HAS_CAPATH -# endif +#if !defined(GNUTLS_VERSION_NUMBER) || (GNUTLS_VERSION_NUMBER < 0x03010a) +#error "too old GnuTLS version" #endif -#if (GNUTLS_VERSION_NUMBER >= 0x030603) -#define HAS_TLS13 -#endif - -#ifdef HAS_OCSP # include -#endif struct ssl_backend_data { gnutls_session_t session; @@ -111,58 +86,10 @@ struct ssl_backend_data { #endif }; -#define BACKEND connssl->backend - -/* - * Custom push and pull callback functions used by GNU TLS to read and write - * to the socket. These functions are simple wrappers to send() and recv() - * (although here using the sread/swrite macros as defined by - * curl_setup_once.h). - * We use custom functions rather than the GNU TLS defaults because it allows - * us to get specific about the fourth "flags" argument, and to use arbitrary - * private data with gnutls_transport_set_ptr if we wish. - * - * When these custom push and pull callbacks fail, GNU TLS checks its own - * session-specific error variable, and when not set also its own global - * errno variable, in order to take appropriate action. GNU TLS does not - * require that the transport is actually a socket. This implies that for - * Windows builds these callbacks should ideally set the session-specific - * error variable using function gnutls_transport_set_errno or as a last - * resort global errno variable using gnutls_transport_set_global_errno, - * with a transport agnostic error value. This implies that some winsock - * error translation must take place in these callbacks. - * - * Paragraph above applies to GNU TLS versions older than 2.12.3, since - * this version GNU TLS does its own internal winsock error translation - * using system_errno() function. - */ - -#if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS) -# define gtls_EINTR 4 -# define gtls_EIO 5 -# define gtls_EAGAIN 11 -static int gtls_mapped_sockerrno(void) -{ - switch(SOCKERRNO) { - case WSAEWOULDBLOCK: - return gtls_EAGAIN; - case WSAEINTR: - return gtls_EINTR; - default: - break; - } - return gtls_EIO; -} -#endif - static ssize_t Curl_gtls_push(void *s, const void *buf, size_t len) { curl_socket_t sock = *(curl_socket_t *)s; ssize_t ret = swrite(sock, buf, len); -#if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS) - if(ret < 0) - gnutls_transport_set_global_errno(gtls_mapped_sockerrno()); -#endif return ret; } @@ -170,10 +97,6 @@ static ssize_t Curl_gtls_pull(void *s, void *buf, size_t len) { curl_socket_t sock = *(curl_socket_t *)s; ssize_t ret = sread(sock, buf, len); -#if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS) - if(ret < 0) - gnutls_transport_set_global_errno(gtls_mapped_sockerrno()); -#endif return ret; } @@ -284,11 +207,12 @@ static CURLcode handshake(struct connectdata *conn, { struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - gnutls_session_t session = BACKEND->session; + struct ssl_backend_data *backend = connssl->backend; + gnutls_session_t session = backend->session; curl_socket_t sockfd = conn->sock[sockindex]; for(;;) { - time_t timeout_ms; + timediff_t timeout_ms; int rc; /* check allowed time left */ @@ -383,51 +307,6 @@ static gnutls_x509_crt_fmt_t do_file_type(const char *type) return -1; } -#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT -static CURLcode -set_ssl_version_min_max(int *list, size_t list_size, struct connectdata *conn) -{ - struct Curl_easy *data = conn->data; - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - long i = ssl_version; - long protocol_priority_idx = 0; - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: -#ifdef HAS_TLS13 - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; -#endif - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - break; - } - - for(; i <= (ssl_version_max >> 16) && - protocol_priority_idx < list_size; ++i) { - switch(i) { - case CURL_SSLVERSION_TLSv1_0: - protocol_priority[protocol_priority_idx++] = GNUTLS_TLS1_0; - break; - case CURL_SSLVERSION_TLSv1_1: - protocol_priority[protocol_priority_idx++] = GNUTLS_TLS1_1; - break; - case CURL_SSLVERSION_TLSv1_2: - protocol_priority[protocol_priority_idx++] = GNUTLS_TLS1_2; - break; - case CURL_SSLVERSION_TLSv1_3: -#ifdef HAS_TLS13 - protocol_priority[protocol_priority_idx++] = GNUTLS_TLS1_3; - break; -#else - failf(data, "GnuTLS: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; -#endif - } - } - return CURLE_OK; -} -#else #define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509" /* If GnuTLS was compiled without support for SRP it will error out if SRP is requested in the priority string, so treat it specially @@ -445,77 +324,59 @@ set_ssl_version_min_max(const char **prioritylist, struct connectdata *conn) ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT; } switch(ssl_version | ssl_version_max) { - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.0:" GNUTLS_SRP; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_1: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.0:+VERS-TLS1.1:" GNUTLS_SRP; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2:" GNUTLS_SRP; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_1: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.1:" GNUTLS_SRP; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_2: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.1:+VERS-TLS1.2:" GNUTLS_SRP; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.2:" GNUTLS_SRP; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_TLSv1_3: -#ifdef HAS_TLS13 - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.3:" GNUTLS_SRP; - return CURLE_OK; -#else - failf(data, "GnuTLS: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; -#endif - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_DEFAULT: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2:" -#ifdef HAS_TLS13 - "+VERS-TLS1.3:" -#endif - GNUTLS_SRP; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_DEFAULT: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.1:+VERS-TLS1.2:" -#ifdef HAS_TLS13 - "+VERS-TLS1.3:" -#endif - GNUTLS_SRP; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.2:" -#ifdef HAS_TLS13 - "+VERS-TLS1.3:" -#endif - GNUTLS_SRP; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_DEFAULT: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.2:" -#ifdef HAS_TLS13 - "+VERS-TLS1.3:" -#endif - GNUTLS_SRP; - return CURLE_OK; + case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.0"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_1: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.0:+VERS-TLS1.1"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_1: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.1"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_2: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.1:+VERS-TLS1.2"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.2"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_TLSv1_3: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.3"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_DEFAULT: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2" + ":+VERS-TLS1.3"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_DEFAULT: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.1:+VERS-TLS1.2" + ":+VERS-TLS1.3"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.2" + ":+VERS-TLS1.3"; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_DEFAULT: + *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" + "+VERS-TLS1.2" + ":+VERS-TLS1.3"; + return CURLE_OK; } failf(data, "GnuTLS: cannot set ssl protocol"); return CURLE_SSL_CONNECT_ERROR; } -#endif static CURLcode gtls_connect_step1(struct connectdata *conn, @@ -523,6 +384,7 @@ gtls_connect_step1(struct connectdata *conn, { struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; unsigned int init_flags; gnutls_session_t session; int rc; @@ -535,28 +397,12 @@ gtls_connect_step1(struct connectdata *conn, #else struct in_addr addr; #endif -#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT - static const int cipher_priority[] = { - /* These two ciphers were added to GnuTLS as late as ver. 3.0.1, - but this code path is only ever used for ver. < 2.12.0. - GNUTLS_CIPHER_AES_128_GCM, - GNUTLS_CIPHER_AES_256_GCM, - */ - GNUTLS_CIPHER_AES_128_CBC, - GNUTLS_CIPHER_AES_256_CBC, - GNUTLS_CIPHER_CAMELLIA_128_CBC, - GNUTLS_CIPHER_CAMELLIA_256_CBC, - GNUTLS_CIPHER_3DES_CBC, - }; - static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; - int protocol_priority[] = { 0, 0, 0, 0 }; -#else const char *prioritylist; const char *err = NULL; -#endif - const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; + long * const certverifyresult = SSL_IS_PROXY() ? + &data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult; if(connssl->state == ssl_connection_complete) /* to make us tolerant against being called more than once for the @@ -566,6 +412,9 @@ gtls_connect_step1(struct connectdata *conn, if(!gtls_inited) Curl_gtls_init(); + /* Initialize certverifyresult to OK */ + *certverifyresult = 0; + if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) { failf(data, "GnuTLS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; @@ -574,7 +423,7 @@ gtls_connect_step1(struct connectdata *conn, sni = FALSE; /* SSLv3 has no SNI */ /* allocate a cred struct */ - rc = gnutls_certificate_allocate_credentials(&BACKEND->cred); + rc = gnutls_certificate_allocate_credentials(&backend->cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; @@ -585,14 +434,14 @@ gtls_connect_step1(struct connectdata *conn, infof(data, "Using TLS-SRP username: %s\n", SSL_SET_OPTION(username)); rc = gnutls_srp_allocate_client_credentials( - &BACKEND->srp_client_cred); + &backend->srp_client_cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_srp_allocate_client_cred() failed: %s", gnutls_strerror(rc)); return CURLE_OUT_OF_MEMORY; } - rc = gnutls_srp_set_client_credentials(BACKEND->srp_client_cred, + rc = gnutls_srp_set_client_credentials(backend->srp_client_cred, SSL_SET_OPTION(username), SSL_SET_OPTION(password)); if(rc != GNUTLS_E_SUCCESS) { @@ -605,52 +454,54 @@ gtls_connect_step1(struct connectdata *conn, if(SSL_CONN_CONFIG(CAfile)) { /* set the trusted CA cert bundle file */ - gnutls_certificate_set_verify_flags(BACKEND->cred, + gnutls_certificate_set_verify_flags(backend->cred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); - rc = gnutls_certificate_set_x509_trust_file(BACKEND->cred, + rc = gnutls_certificate_set_x509_trust_file(backend->cred, SSL_CONN_CONFIG(CAfile), GNUTLS_X509_FMT_PEM); if(rc < 0) { infof(data, "error reading ca cert file %s (%s)\n", SSL_CONN_CONFIG(CAfile), gnutls_strerror(rc)); - if(SSL_CONN_CONFIG(verifypeer)) + if(SSL_CONN_CONFIG(verifypeer)) { + *certverifyresult = rc; return CURLE_SSL_CACERT_BADFILE; + } } else infof(data, "found %d certificates in %s\n", rc, SSL_CONN_CONFIG(CAfile)); } -#ifdef HAS_CAPATH if(SSL_CONN_CONFIG(CApath)) { /* set the trusted CA cert directory */ - rc = gnutls_certificate_set_x509_trust_dir(BACKEND->cred, + rc = gnutls_certificate_set_x509_trust_dir(backend->cred, SSL_CONN_CONFIG(CApath), GNUTLS_X509_FMT_PEM); if(rc < 0) { infof(data, "error reading ca cert file %s (%s)\n", SSL_CONN_CONFIG(CApath), gnutls_strerror(rc)); - if(SSL_CONN_CONFIG(verifypeer)) + if(SSL_CONN_CONFIG(verifypeer)) { + *certverifyresult = rc; return CURLE_SSL_CACERT_BADFILE; + } } else infof(data, "found %d certificates in %s\n", rc, SSL_CONN_CONFIG(CApath)); } -#endif #ifdef CURL_CA_FALLBACK /* use system ca certificate store as fallback */ if(SSL_CONN_CONFIG(verifypeer) && !(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath))) { - gnutls_certificate_set_x509_system_trust(BACKEND->cred); + gnutls_certificate_set_x509_system_trust(backend->cred); } #endif if(SSL_SET_OPTION(CRLfile)) { /* set the CRL list file */ - rc = gnutls_certificate_set_x509_crl_file(BACKEND->cred, + rc = gnutls_certificate_set_x509_crl_file(backend->cred, SSL_SET_OPTION(CRLfile), GNUTLS_X509_FMT_PEM); if(rc < 0) { @@ -666,19 +517,23 @@ gtls_connect_step1(struct connectdata *conn, /* Initialize TLS session as a client */ init_flags = GNUTLS_CLIENT; +#if defined(GNUTLS_FORCE_CLIENT_CERT) + init_flags |= GNUTLS_FORCE_CLIENT_CERT; +#endif + #if defined(GNUTLS_NO_TICKETS) /* Disable TLS session tickets */ init_flags |= GNUTLS_NO_TICKETS; #endif - rc = gnutls_init(&BACKEND->session, init_flags); + rc = gnutls_init(&backend->session, init_flags); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_init() failed: %d", rc); return CURLE_SSL_CONNECT_ERROR; } /* convenient assign */ - session = BACKEND->session; + session = backend->session; if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && #ifdef ENABLE_IPV6 @@ -695,62 +550,6 @@ gtls_connect_step1(struct connectdata *conn, if(rc != GNUTLS_E_SUCCESS) return CURLE_SSL_CONNECT_ERROR; -#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT - rc = gnutls_cipher_set_priority(session, cipher_priority); - if(rc != GNUTLS_E_SUCCESS) - return CURLE_SSL_CONNECT_ERROR; - - /* Sets the priority on the certificate types supported by gnutls. Priority - is higher for types specified before others. After specifying the types - you want, you must append a 0. */ - rc = gnutls_certificate_type_set_priority(session, cert_type_priority); - if(rc != GNUTLS_E_SUCCESS) - return CURLE_SSL_CONNECT_ERROR; - - if(SSL_CONN_CONFIG(cipher_list) != NULL) { - failf(data, "can't pass a custom cipher list to older GnuTLS" - " versions"); - return CURLE_SSL_CONNECT_ERROR; - } - - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_SSLv3: - protocol_priority[0] = GNUTLS_SSL3; - break; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - protocol_priority[0] = GNUTLS_TLS1_0; - protocol_priority[1] = GNUTLS_TLS1_1; - protocol_priority[2] = GNUTLS_TLS1_2; -#ifdef HAS_TLS13 - protocol_priority[3] = GNUTLS_TLS1_3; -#endif - break; - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - CURLcode result = set_ssl_version_min_max(protocol_priority, - sizeof(protocol_priority)/sizeof(protocol_priority[0]), conn); - if(result != CURLE_OK) - return result; - break; - } - case CURL_SSLVERSION_SSLv2: - failf(data, "GnuTLS does not support SSLv2"); - return CURLE_SSL_CONNECT_ERROR; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - rc = gnutls_protocol_set_priority(session, protocol_priority); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "Did you pass a valid GnuTLS cipher list?"); - return CURLE_SSL_CONNECT_ERROR; - } - -#else /* Ensure +SRP comes at the *end* of all relevant strings so that it can be * removed if a run-time error indicates that SRP is not supported by this * GnuTLS version */ @@ -760,11 +559,11 @@ gtls_connect_step1(struct connectdata *conn, break; case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: - prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:" + prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0" #ifdef HAS_TLS13 - "+VERS-TLS1.3:" + ":+VERS-TLS1.3" #endif - GNUTLS_SRP; + ; break; case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: @@ -783,32 +582,39 @@ gtls_connect_step1(struct connectdata *conn, failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); return CURLE_SSL_CONNECT_ERROR; } - rc = gnutls_priority_set_direct(session, prioritylist, &err); - if((rc == GNUTLS_E_INVALID_REQUEST) && err) { - if(!strcmp(err, GNUTLS_SRP)) { - /* This GnuTLS was probably compiled without support for SRP. - * Note that fact and try again without it. */ - int validprioritylen = curlx_uztosi(err - prioritylist); - char *prioritycopy = strdup(prioritylist); - if(!prioritycopy) - return CURLE_OUT_OF_MEMORY; +#ifdef USE_TLS_SRP + /* Only add SRP to the cipher list if SRP is requested. Otherwise + * GnuTLS will disable TLS 1.3 support. */ + if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) { + size_t len = strlen(prioritylist); + + char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1); + if(!prioritysrp) + return CURLE_OUT_OF_MEMORY; + strcpy(prioritysrp, prioritylist); + strcpy(prioritysrp + len, ":" GNUTLS_SRP); + + rc = gnutls_priority_set_direct(session, prioritysrp, &err); + free(prioritysrp); + + if((rc == GNUTLS_E_INVALID_REQUEST) && err) { infof(data, "This GnuTLS does not support SRP\n"); - if(validprioritylen) - /* Remove the :+SRP */ - prioritycopy[validprioritylen - 1] = 0; - rc = gnutls_priority_set_direct(session, prioritycopy, &err); - free(prioritycopy); } } + else { +#endif + rc = gnutls_priority_set_direct(session, prioritylist, &err); +#ifdef USE_TLS_SRP + } +#endif + if(rc != GNUTLS_E_SUCCESS) { failf(data, "Error %d setting GnuTLS cipher list starting with %s", rc, err); return CURLE_SSL_CONNECT_ERROR; } -#endif -#ifdef HAS_ALPN if(conn->bits.tls_enable_alpn) { int cur = 0; gnutls_datum_t protocols[2]; @@ -830,18 +636,16 @@ gtls_connect_step1(struct connectdata *conn, gnutls_alpn_set_protocols(session, protocols, cur, 0); } -#endif if(SSL_SET_OPTION(cert)) { if(SSL_SET_OPTION(key_passwd)) { -#if HAVE_GNUTLS_CERTIFICATE_SET_X509_KEY_FILE2 const unsigned int supported_key_encryption_algorithms = GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR | GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES | GNUTLS_PKCS_USE_PBES2_AES_128 | GNUTLS_PKCS_USE_PBES2_AES_192 | GNUTLS_PKCS_USE_PBES2_AES_256; rc = gnutls_certificate_set_x509_key_file2( - BACKEND->cred, + backend->cred, SSL_SET_OPTION(cert), SSL_SET_OPTION(key) ? SSL_SET_OPTION(key) : SSL_SET_OPTION(cert), @@ -854,14 +658,10 @@ gtls_connect_step1(struct connectdata *conn, gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } -#else - failf(data, "gnutls lacks support for encrypted key files"); - return CURLE_SSL_CONNECT_ERROR; -#endif } else { if(gnutls_certificate_set_x509_key_file( - BACKEND->cred, + backend->cred, SSL_SET_OPTION(cert), SSL_SET_OPTION(key) ? SSL_SET_OPTION(key) : SSL_SET_OPTION(cert), @@ -877,7 +677,7 @@ gtls_connect_step1(struct connectdata *conn, /* put the credentials to the current session */ if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) { rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP, - BACKEND->srp_client_cred); + backend->srp_client_cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; @@ -887,7 +687,7 @@ gtls_connect_step1(struct connectdata *conn, #endif { rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, - BACKEND->cred); + backend->cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; @@ -913,10 +713,6 @@ gtls_connect_step1(struct connectdata *conn, gnutls_transport_set_push_function(session, gnutls_transport_push); gnutls_transport_set_pull_function(session, gnutls_transport_pull); - /* lowat must be set to zero when using custom push and pull functions. */ - gnutls_transport_set_lowat(session, 0); - -#ifdef HAS_OCSP if(SSL_CONN_CONFIG(verifystatus)) { rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL); if(rc != GNUTLS_E_SUCCESS) { @@ -924,7 +720,6 @@ gtls_connect_step1(struct connectdata *conn, return CURLE_SSL_CONNECT_ERROR; } } -#endif /* This might be a reconnect, so we check for a session ID in the cache to speed up things */ @@ -1016,17 +811,17 @@ gtls_connect_step3(struct connectdata *conn, unsigned int verify_status = 0; gnutls_x509_crt_t x509_cert, x509_issuer; gnutls_datum_t issuerp; - char certbuf[256] = ""; /* big enough? */ + gnutls_datum_t certfields; + char certname[65] = ""; /* limited to 64 chars by ASN.1 */ size_t size; time_t certclock; const char *ptr; struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - gnutls_session_t session = BACKEND->session; + struct ssl_backend_data *backend = connssl->backend; + gnutls_session_t session = backend->session; int rc; -#ifdef HAS_ALPN gnutls_datum_t proto; -#endif CURLcode result = CURLE_OK; #ifndef CURL_DISABLE_VERBOSE_STRINGS unsigned int algo; @@ -1035,6 +830,8 @@ gtls_connect_step3(struct connectdata *conn, #endif const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; + long * const certverifyresult = SSL_IS_PROXY() ? + &data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult; /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */ ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session), @@ -1066,6 +863,7 @@ gtls_connect_step3(struct connectdata *conn, else { #endif failf(data, "failed to get server cert"); + *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; return CURLE_PEER_FAILED_VERIFICATION; #ifdef USE_TLS_SRP } @@ -1102,9 +900,12 @@ gtls_connect_step3(struct connectdata *conn, rc = gnutls_certificate_verify_peers2(session, &verify_status); if(rc < 0) { failf(data, "server cert verify failed: %d", rc); + *certverifyresult = rc; return CURLE_SSL_CONNECT_ERROR; } + *certverifyresult = verify_status; + /* verify_status is a bitmask of gnutls_certificate_status bits */ if(verify_status & GNUTLS_CERT_INVALID) { if(SSL_CONN_CONFIG(verifypeer)) { @@ -1123,7 +924,6 @@ gtls_connect_step3(struct connectdata *conn, else infof(data, "\t server certificate verification SKIPPED\n"); -#ifdef HAS_OCSP if(SSL_CONN_CONFIG(verifystatus)) { if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) { gnutls_datum_t status_request; @@ -1226,7 +1026,6 @@ gtls_connect_step3(struct connectdata *conn, } else infof(data, "\t server certificate status verification SKIPPED\n"); -#endif /* initialize an X.509 certificate structure. */ gnutls_x509_crt_init(&x509_cert); @@ -1253,11 +1052,11 @@ gtls_connect_step3(struct connectdata *conn, SSL_SET_OPTION(issuercert)?SSL_SET_OPTION(issuercert):"none"); } - size = sizeof(certbuf); + size = sizeof(certname); rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME, 0, /* the first and only one */ FALSE, - certbuf, + certname, &size); if(rc) { infof(data, "error fetching CN from cert:%s\n", @@ -1318,16 +1117,16 @@ gtls_connect_step3(struct connectdata *conn, if(SSL_CONN_CONFIG(verifyhost)) { failf(data, "SSL: certificate subject name (%s) does not match " - "target host name '%s'", certbuf, dispname); + "target host name '%s'", certname, dispname); gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\t common name: %s (does not match '%s')\n", - certbuf, dispname); + certname, dispname); } else - infof(data, "\t common name: %s (matched)\n", certbuf); + infof(data, "\t common name: %s (matched)\n", certname); /* Check for time-based validity */ certclock = gnutls_x509_crt_get_expiration_time(x509_cert); @@ -1335,6 +1134,7 @@ gtls_connect_step3(struct connectdata *conn, if(certclock == (time_t)-1) { if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "server cert expiration date verify failed"); + *certverifyresult = GNUTLS_CERT_EXPIRED; gnutls_x509_crt_deinit(x509_cert); return CURLE_SSL_CONNECT_ERROR; } @@ -1345,6 +1145,7 @@ gtls_connect_step3(struct connectdata *conn, if(certclock < time(NULL)) { if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "server certificate expiration date has passed."); + *certverifyresult = GNUTLS_CERT_EXPIRED; gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } @@ -1360,6 +1161,7 @@ gtls_connect_step3(struct connectdata *conn, if(certclock == (time_t)-1) { if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "server cert activation date verify failed"); + *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED; gnutls_x509_crt_deinit(x509_cert); return CURLE_SSL_CONNECT_ERROR; } @@ -1370,6 +1172,7 @@ gtls_connect_step3(struct connectdata *conn, if(certclock > time(NULL)) { if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "server certificate not activated yet."); + *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED; gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } @@ -1412,9 +1215,10 @@ gtls_connect_step3(struct connectdata *conn, gnutls_x509_crt_get_version(x509_cert)); - size = sizeof(certbuf); - gnutls_x509_crt_get_dn(x509_cert, certbuf, &size); - infof(data, "\t subject: %s\n", certbuf); + rc = gnutls_x509_crt_get_dn2(x509_cert, &certfields); + if(rc != 0) + return CURLE_OUT_OF_MEMORY; + infof(data, "\t subject: %s\n", certfields.data); certclock = gnutls_x509_crt_get_activation_time(x509_cert); showtime(data, "start date", certclock); @@ -1422,14 +1226,14 @@ gtls_connect_step3(struct connectdata *conn, certclock = gnutls_x509_crt_get_expiration_time(x509_cert); showtime(data, "expire date", certclock); - size = sizeof(certbuf); - gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size); - infof(data, "\t issuer: %s\n", certbuf); + rc = gnutls_x509_crt_get_issuer_dn2(x509_cert, &certfields); + if(rc != 0) + return CURLE_OUT_OF_MEMORY; + infof(data, "\t issuer: %s\n", certfields.data); #endif gnutls_x509_crt_deinit(x509_cert); -#ifdef HAS_ALPN if(conn->bits.tls_enable_alpn) { rc = gnutls_alpn_get_selected_protocol(session, &proto); if(rc == 0) { @@ -1455,7 +1259,6 @@ gtls_connect_step3(struct connectdata *conn, Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } -#endif conn->ssl[sockindex].state = ssl_connection_complete; conn->recv[sockindex] = gtls_recv; @@ -1573,13 +1376,14 @@ static bool Curl_gtls_data_pending(const struct connectdata *conn, { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; bool res = FALSE; - if(BACKEND->session && - 0 != gnutls_record_check_pending(BACKEND->session)) + struct ssl_backend_data *backend = connssl->backend; + if(backend->session && + 0 != gnutls_record_check_pending(backend->session)) res = TRUE; connssl = &conn->proxy_ssl[connindex]; - if(BACKEND->session && - 0 != gnutls_record_check_pending(BACKEND->session)) + if(backend->session && + 0 != gnutls_record_check_pending(backend->session)) res = TRUE; return res; @@ -1592,7 +1396,8 @@ static ssize_t gtls_send(struct connectdata *conn, CURLcode *curlcode) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - ssize_t rc = gnutls_record_send(BACKEND->session, mem, len); + struct ssl_backend_data *backend = connssl->backend; + ssize_t rc = gnutls_record_send(backend->session, mem, len); if(rc < 0) { *curlcode = (rc == GNUTLS_E_AGAIN) @@ -1607,19 +1412,20 @@ static ssize_t gtls_send(struct connectdata *conn, static void close_one(struct ssl_connect_data *connssl) { - if(BACKEND->session) { - gnutls_bye(BACKEND->session, GNUTLS_SHUT_RDWR); - gnutls_deinit(BACKEND->session); - BACKEND->session = NULL; + struct ssl_backend_data *backend = connssl->backend; + if(backend->session) { + gnutls_bye(backend->session, GNUTLS_SHUT_WR); + gnutls_deinit(backend->session); + backend->session = NULL; } - if(BACKEND->cred) { - gnutls_certificate_free_credentials(BACKEND->cred); - BACKEND->cred = NULL; + if(backend->cred) { + gnutls_certificate_free_credentials(backend->cred); + backend->cred = NULL; } #ifdef USE_TLS_SRP - if(BACKEND->srp_client_cred) { - gnutls_srp_free_client_credentials(BACKEND->srp_client_cred); - BACKEND->srp_client_cred = NULL; + if(backend->srp_client_cred) { + gnutls_srp_free_client_credentials(backend->srp_client_cred); + backend->srp_client_cred = NULL; } #endif } @@ -1637,6 +1443,7 @@ static void Curl_gtls_close(struct connectdata *conn, int sockindex) static int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; int retval = 0; struct Curl_easy *data = conn->data; @@ -1647,10 +1454,10 @@ static int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) we do not send one. Let's hope other servers do the same... */ if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - gnutls_bye(BACKEND->session, GNUTLS_SHUT_WR); + gnutls_bye(backend->session, GNUTLS_SHUT_WR); #endif - if(BACKEND->session) { + if(backend->session) { ssize_t result; bool done = FALSE; char buf[120]; @@ -1661,7 +1468,7 @@ static int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) if(what > 0) { /* Something to read, let's do it and hope that it is the close notify alert from the server */ - result = gnutls_record_recv(BACKEND->session, + result = gnutls_record_recv(backend->session, buf, sizeof(buf)); switch(result) { case 0: @@ -1691,18 +1498,18 @@ static int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) done = TRUE; } } - gnutls_deinit(BACKEND->session); + gnutls_deinit(backend->session); } - gnutls_certificate_free_credentials(BACKEND->cred); + gnutls_certificate_free_credentials(backend->cred); #ifdef USE_TLS_SRP if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP && SSL_SET_OPTION(username) != NULL) - gnutls_srp_free_client_credentials(BACKEND->srp_client_cred); + gnutls_srp_free_client_credentials(backend->srp_client_cred); #endif - BACKEND->cred = NULL; - BACKEND->session = NULL; + backend->cred = NULL; + backend->session = NULL; return retval; } @@ -1714,9 +1521,10 @@ static ssize_t gtls_recv(struct connectdata *conn, /* connection data */ CURLcode *curlcode) { struct ssl_connect_data *connssl = &conn->ssl[num]; + struct ssl_backend_data *backend = connssl->backend; ssize_t ret; - ret = gnutls_record_recv(BACKEND->session, buf, buffersize); + ret = gnutls_record_recv(backend->session, buf, buffersize); if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { *curlcode = CURLE_AGAIN; return -1; @@ -1832,18 +1640,15 @@ static CURLcode Curl_gtls_sha256sum(const unsigned char *tmp, /* input */ static bool Curl_gtls_cert_status_request(void) { -#ifdef HAS_OCSP return TRUE; -#else - return FALSE; -#endif } static void *Curl_gtls_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { + struct ssl_backend_data *backend = connssl->backend; (void)info; - return BACKEND->session; + return backend->session; } const struct Curl_ssl Curl_ssl_gnutls = { diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c new file mode 100644 index 00000000000..70d22ecf0b0 --- /dev/null +++ b/Utilities/cmcurl/lib/vtls/keylog.c @@ -0,0 +1,156 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "keylog.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1) + +#define CLIENT_RANDOM_SIZE 32 + +/* + * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the + * secret size depends on the cipher suite's hash function which is 32 bytes + * for SHA-256 and 48 bytes for SHA-384. + */ +#define SECRET_MAXLEN 48 + + +/* The fp for the open SSLKEYLOGFILE, or NULL if not open */ +static FILE *keylog_file_fp; + +void +Curl_tls_keylog_open(void) +{ + char *keylog_file_name; + + if(!keylog_file_fp) { + keylog_file_name = curl_getenv("SSLKEYLOGFILE"); + if(keylog_file_name) { + keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); + if(keylog_file_fp) { +#ifdef WIN32 + if(setvbuf(keylog_file_fp, NULL, _IONBF, 0)) +#else + if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) +#endif + { + fclose(keylog_file_fp); + keylog_file_fp = NULL; + } + } + Curl_safefree(keylog_file_name); + } + } +} + +void +Curl_tls_keylog_close(void) +{ + if(keylog_file_fp) { + fclose(keylog_file_fp); + keylog_file_fp = NULL; + } +} + +bool +Curl_tls_keylog_enabled(void) +{ + return keylog_file_fp != NULL; +} + +bool +Curl_tls_keylog_write_line(const char *line) +{ + /* The current maximum valid keylog line length LF and NUL is 195. */ + size_t linelen; + char buf[256]; + + if(!keylog_file_fp || !line) { + return false; + } + + linelen = strlen(line); + if(linelen == 0 || linelen > sizeof(buf) - 2) { + /* Empty line or too big to fit in a LF and NUL. */ + return false; + } + + memcpy(buf, line, linelen); + if(line[linelen - 1] != '\n') { + buf[linelen++] = '\n'; + } + buf[linelen] = '\0'; + + /* Using fputs here instead of fprintf since libcurl's fprintf replacement + may not be thread-safe. */ + fputs(buf, keylog_file_fp); + return true; +} + +bool +Curl_tls_keylog_write(const char *label, + const unsigned char client_random[CLIENT_RANDOM_SIZE], + const unsigned char *secret, size_t secretlen) +{ + const char *hex = "0123456789ABCDEF"; + size_t pos, i; + char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 + + 2 * SECRET_MAXLEN + 1 + 1]; + + if(!keylog_file_fp) { + return false; + } + + pos = strlen(label); + if(pos > KEYLOG_LABEL_MAXLEN || !secretlen || secretlen > SECRET_MAXLEN) { + /* Should never happen - sanity check anyway. */ + return false; + } + + memcpy(line, label, pos); + line[pos++] = ' '; + + /* Client Random */ + for(i = 0; i < CLIENT_RANDOM_SIZE; i++) { + line[pos++] = hex[client_random[i] >> 4]; + line[pos++] = hex[client_random[i] & 0xF]; + } + line[pos++] = ' '; + + /* Secret */ + for(i = 0; i < secretlen; i++) { + line[pos++] = hex[secret[i] >> 4]; + line[pos++] = hex[secret[i] & 0xF]; + } + line[pos++] = '\n'; + line[pos] = '\0'; + + /* Using fputs here instead of fprintf since libcurl's fprintf replacement + may not be thread-safe. */ + fputs(line, keylog_file_fp); + return true; +} diff --git a/Utilities/cmcurl/lib/vtls/keylog.h b/Utilities/cmcurl/lib/vtls/keylog.h new file mode 100644 index 00000000000..c6b99db938a --- /dev/null +++ b/Utilities/cmcurl/lib/vtls/keylog.h @@ -0,0 +1,56 @@ +#ifndef HEADER_CURL_KEYLOG_H +#define HEADER_CURL_KEYLOG_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.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. + * + ***************************************************************************/ +#include "curl_setup.h" + +/* + * Opens the TLS key log file if requested by the user. The SSLKEYLOGFILE + * environment variable specifies the output file. + */ +void Curl_tls_keylog_open(void); + +/* + * Closes the TLS key log file if not already. + */ +void Curl_tls_keylog_close(void); + +/* + * Returns true if the user successfully enabled the TLS key log file. + */ +bool Curl_tls_keylog_enabled(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); + +/* + * Appends a line to the key log file, ensure it is terminated by a LF. + * Returns true iff the key log file is open and a valid line was provided. + */ +bool Curl_tls_keylog_write_line(const char *line); + +#endif /* HEADER_CURL_KEYLOG_H */ diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index 63d1f4c81b9..545f824c62c 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2010 - 2011, Hoi-Ho Chan, - * Copyright (C) 2012 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -55,7 +55,7 @@ #include "connect.h" /* for the connect timeout */ #include "select.h" #include "multiif.h" -#include "polarssl_threadlock.h" +#include "mbedtls_threadlock.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -75,8 +75,6 @@ struct ssl_backend_data { const char *protocols[3]; }; -#define BACKEND connssl->backend - /* apply threading? */ #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) #define THREADING_SUPPORT @@ -91,12 +89,12 @@ static int entropy_init_initialized = 0; static void entropy_init_mutex(mbedtls_entropy_context *ctx) { /* lock 0 = entropy_init_mutex() */ - Curl_polarsslthreadlock_lock_function(0); + Curl_mbedtlsthreadlock_lock_function(0); if(entropy_init_initialized == 0) { mbedtls_entropy_init(ctx); entropy_init_initialized = 1; } - Curl_polarsslthreadlock_unlock_function(0); + Curl_mbedtlsthreadlock_unlock_function(0); } /* end of entropy_init_mutex() */ @@ -105,9 +103,9 @@ static int entropy_func_mutex(void *data, unsigned char *output, size_t len) { int ret; /* lock 1 = entropy_func_mutex() */ - Curl_polarsslthreadlock_lock_function(1); + Curl_mbedtlsthreadlock_lock_function(1); ret = mbedtls_entropy_func(data, output, len); - Curl_polarsslthreadlock_unlock_function(1); + Curl_mbedtlsthreadlock_unlock_function(1); return ret; } @@ -196,6 +194,7 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) { struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_1; int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_1; long ssl_version = SSL_CONN_CONFIG(version); @@ -227,9 +226,9 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) return result; } - mbedtls_ssl_conf_min_version(&BACKEND->config, MBEDTLS_SSL_MAJOR_VERSION_3, + mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, mbedtls_ver_min); - mbedtls_ssl_conf_max_version(&BACKEND->config, MBEDTLS_SSL_MAJOR_VERSION_3, + mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, mbedtls_ver_max); return result; @@ -240,15 +239,21 @@ mbed_connect_step1(struct connectdata *conn, int sockindex) { struct Curl_easy *data = conn->data; - struct ssl_connect_data* connssl = &conn->ssl[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); const char * const ssl_capath = SSL_CONN_CONFIG(CApath); char * const ssl_cert = SSL_SET_OPTION(cert); const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile); +#ifndef CURL_DISABLE_PROXY const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; +#else + const char * const hostname = conn->host.name; + const long int port = conn->remote_port; +#endif int ret = -1; char errorbuf[128]; errorbuf[0] = 0; @@ -261,9 +266,9 @@ mbed_connect_step1(struct connectdata *conn, #ifdef THREADING_SUPPORT entropy_init_mutex(&ts_entropy); - mbedtls_ctr_drbg_init(&BACKEND->ctr_drbg); + mbedtls_ctr_drbg_init(&backend->ctr_drbg); - ret = mbedtls_ctr_drbg_seed(&BACKEND->ctr_drbg, entropy_func_mutex, + ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex, &ts_entropy, NULL, 0); if(ret) { #ifdef MBEDTLS_ERROR_C @@ -273,11 +278,11 @@ mbed_connect_step1(struct connectdata *conn, -ret, errorbuf); } #else - mbedtls_entropy_init(&BACKEND->entropy); - mbedtls_ctr_drbg_init(&BACKEND->ctr_drbg); + mbedtls_entropy_init(&backend->entropy); + mbedtls_ctr_drbg_init(&backend->ctr_drbg); - ret = mbedtls_ctr_drbg_seed(&BACKEND->ctr_drbg, mbedtls_entropy_func, - &BACKEND->entropy, NULL, 0); + ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, mbedtls_entropy_func, + &backend->entropy, NULL, 0); if(ret) { #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); @@ -288,10 +293,10 @@ mbed_connect_step1(struct connectdata *conn, #endif /* THREADING_SUPPORT */ /* Load the trusted CA */ - mbedtls_x509_crt_init(&BACKEND->cacert); + mbedtls_x509_crt_init(&backend->cacert); if(ssl_cafile) { - ret = mbedtls_x509_crt_parse_file(&BACKEND->cacert, ssl_cafile); + ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile); if(ret<0) { #ifdef MBEDTLS_ERROR_C @@ -306,7 +311,7 @@ mbed_connect_step1(struct connectdata *conn, } if(ssl_capath) { - ret = mbedtls_x509_crt_parse_path(&BACKEND->cacert, ssl_capath); + ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath); if(ret<0) { #ifdef MBEDTLS_ERROR_C @@ -321,10 +326,10 @@ mbed_connect_step1(struct connectdata *conn, } /* Load the client certificate */ - mbedtls_x509_crt_init(&BACKEND->clicert); + mbedtls_x509_crt_init(&backend->clicert); if(ssl_cert) { - ret = mbedtls_x509_crt_parse_file(&BACKEND->clicert, ssl_cert); + ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert); if(ret) { #ifdef MBEDTLS_ERROR_C @@ -338,13 +343,13 @@ mbed_connect_step1(struct connectdata *conn, } /* Load the client private key */ - mbedtls_pk_init(&BACKEND->pk); + mbedtls_pk_init(&backend->pk); if(SSL_SET_OPTION(key)) { - ret = mbedtls_pk_parse_keyfile(&BACKEND->pk, SSL_SET_OPTION(key), + ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key), SSL_SET_OPTION(key_passwd)); - if(ret == 0 && !(mbedtls_pk_can_do(&BACKEND->pk, MBEDTLS_PK_RSA) || - mbedtls_pk_can_do(&BACKEND->pk, MBEDTLS_PK_ECKEY))) + if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) || + mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; if(ret) { @@ -359,10 +364,10 @@ mbed_connect_step1(struct connectdata *conn, } /* Load the CRL */ - mbedtls_x509_crl_init(&BACKEND->crl); + mbedtls_x509_crl_init(&backend->crl); if(ssl_crlfile) { - ret = mbedtls_x509_crl_parse_file(&BACKEND->crl, ssl_crlfile); + ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); if(ret) { #ifdef MBEDTLS_ERROR_C @@ -377,14 +382,14 @@ mbed_connect_step1(struct connectdata *conn, infof(data, "mbedTLS: Connecting to %s:%ld\n", hostname, port); - mbedtls_ssl_config_init(&BACKEND->config); + mbedtls_ssl_config_init(&backend->config); - mbedtls_ssl_init(&BACKEND->ssl); - if(mbedtls_ssl_setup(&BACKEND->ssl, &BACKEND->config)) { + mbedtls_ssl_init(&backend->ssl); + if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) { failf(data, "mbedTLS: ssl_init failed"); return CURLE_SSL_CONNECT_ERROR; } - ret = mbedtls_ssl_config_defaults(&BACKEND->config, + ret = mbedtls_ssl_config_defaults(&backend->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); @@ -394,20 +399,20 @@ mbed_connect_step1(struct connectdata *conn, } /* new profile with RSA min key len = 1024 ... */ - mbedtls_ssl_conf_cert_profile(&BACKEND->config, + mbedtls_ssl_conf_cert_profile(&backend->config, &mbedtls_x509_crt_profile_fr); switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: - mbedtls_ssl_conf_min_version(&BACKEND->config, MBEDTLS_SSL_MAJOR_VERSION_3, + mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_1); infof(data, "mbedTLS: Set min SSL version to TLS 1.0\n"); break; case CURL_SSLVERSION_SSLv3: - mbedtls_ssl_conf_min_version(&BACKEND->config, MBEDTLS_SSL_MAJOR_VERSION_3, + mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); - mbedtls_ssl_conf_max_version(&BACKEND->config, MBEDTLS_SSL_MAJOR_VERSION_3, + mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); infof(data, "mbedTLS: Set SSL version to SSLv3\n"); break; @@ -426,25 +431,25 @@ mbed_connect_step1(struct connectdata *conn, return CURLE_SSL_CONNECT_ERROR; } - mbedtls_ssl_conf_authmode(&BACKEND->config, MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); - mbedtls_ssl_conf_rng(&BACKEND->config, mbedtls_ctr_drbg_random, - &BACKEND->ctr_drbg); - mbedtls_ssl_set_bio(&BACKEND->ssl, &conn->sock[sockindex], + mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random, + &backend->ctr_drbg); + mbedtls_ssl_set_bio(&backend->ssl, &conn->sock[sockindex], mbedtls_net_send, mbedtls_net_recv, NULL /* rev_timeout() */); - mbedtls_ssl_conf_ciphersuites(&BACKEND->config, + mbedtls_ssl_conf_ciphersuites(&backend->config, mbedtls_ssl_list_ciphersuites()); #if defined(MBEDTLS_SSL_RENEGOTIATION) - mbedtls_ssl_conf_renegotiation(&BACKEND->config, + mbedtls_ssl_conf_renegotiation(&backend->config, MBEDTLS_SSL_RENEGOTIATION_ENABLED); #endif #if defined(MBEDTLS_SSL_SESSION_TICKETS) - mbedtls_ssl_conf_session_tickets(&BACKEND->config, + mbedtls_ssl_conf_session_tickets(&backend->config, MBEDTLS_SSL_SESSION_TICKETS_DISABLED); #endif @@ -454,7 +459,7 @@ mbed_connect_step1(struct connectdata *conn, Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &old_session, NULL, sockindex)) { - ret = mbedtls_ssl_set_session(&BACKEND->ssl, old_session); + ret = mbedtls_ssl_set_session(&backend->ssl, old_session); if(ret) { Curl_ssl_sessionid_unlock(conn); failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); @@ -465,15 +470,15 @@ mbed_connect_step1(struct connectdata *conn, Curl_ssl_sessionid_unlock(conn); } - mbedtls_ssl_conf_ca_chain(&BACKEND->config, - &BACKEND->cacert, - &BACKEND->crl); + mbedtls_ssl_conf_ca_chain(&backend->config, + &backend->cacert, + &backend->crl); if(SSL_SET_OPTION(key)) { - mbedtls_ssl_conf_own_cert(&BACKEND->config, - &BACKEND->clicert, &BACKEND->pk); + mbedtls_ssl_conf_own_cert(&backend->config, + &backend->clicert, &backend->pk); } - if(mbedtls_ssl_set_hostname(&BACKEND->ssl, hostname)) { + if(mbedtls_ssl_set_hostname(&backend->ssl, 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. */ @@ -483,7 +488,7 @@ mbed_connect_step1(struct connectdata *conn, #ifdef HAS_ALPN if(conn->bits.tls_enable_alpn) { - const char **p = &BACKEND->protocols[0]; + const char **p = &backend->protocols[0]; #ifdef USE_NGHTTP2 if(data->set.httpversion >= CURL_HTTP_VERSION_2) *p++ = NGHTTP2_PROTO_VERSION_ID; @@ -492,19 +497,19 @@ mbed_connect_step1(struct connectdata *conn, *p = NULL; /* this function doesn't clone the protocols array, which is why we need to keep it around */ - if(mbedtls_ssl_conf_alpn_protocols(&BACKEND->config, - &BACKEND->protocols[0])) { + if(mbedtls_ssl_conf_alpn_protocols(&backend->config, + &backend->protocols[0])) { failf(data, "Failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } - for(p = &BACKEND->protocols[0]; *p; ++p) + for(p = &backend->protocols[0]; *p; ++p) infof(data, "ALPN, offering %s\n", *p); } #endif #ifdef MBEDTLS_DEBUG /* In order to make that work in mbedtls MBEDTLS_DEBUG_C must be defined. */ - mbedtls_ssl_conf_dbg(&BACKEND->config, mbed_debug, data); + mbedtls_ssl_conf_dbg(&backend->config, mbed_debug, data); /* - 0 No debug * - 1 Error * - 2 State change @@ -516,7 +521,7 @@ mbed_connect_step1(struct connectdata *conn, /* give application a chance to interfere with mbedTLS set up. */ if(data->set.ssl.fsslctx) { - ret = (*data->set.ssl.fsslctx)(data, &BACKEND->config, + ret = (*data->set.ssl.fsslctx)(data, &backend->config, data->set.ssl.fsslctxp); if(ret) { failf(data, "error signaled by ssl ctx callback"); @@ -535,16 +540,22 @@ mbed_connect_step2(struct connectdata *conn, { int ret; struct Curl_easy *data = conn->data; - struct ssl_connect_data* connssl = &conn->ssl[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; const mbedtls_x509_crt *peercert; +#ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = SSL_IS_PROXY() ? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : - data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : + data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; +#else + const char * const pinnedpubkey = + data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; +#endif conn->recv[sockindex] = mbed_recv; conn->send[sockindex] = mbed_send; - ret = mbedtls_ssl_handshake(&BACKEND->ssl); + ret = mbedtls_ssl_handshake(&backend->ssl); if(ret == MBEDTLS_ERR_SSL_WANT_READ) { connssl->connecting_state = ssl_connect_2_reading; @@ -566,10 +577,10 @@ mbed_connect_step2(struct connectdata *conn, } infof(data, "mbedTLS: Handshake complete, cipher is %s\n", - mbedtls_ssl_get_ciphersuite(&BACKEND->ssl) + mbedtls_ssl_get_ciphersuite(&backend->ssl) ); - ret = mbedtls_ssl_get_verify_result(&BACKEND->ssl); + ret = mbedtls_ssl_get_verify_result(&backend->ssl); if(!SSL_CONN_CONFIG(verifyhost)) /* Ignore hostname errors if verifyhost is disabled */ @@ -588,10 +599,13 @@ mbed_connect_step2(struct connectdata *conn, else if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED) failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); + else if(ret & MBEDTLS_X509_BADCERT_FUTURE) + failf(data, "Cert verify failed: BADCERT_FUTURE"); + return CURLE_PEER_FAILED_VERIFICATION; } - peercert = mbedtls_ssl_get_peer_cert(&BACKEND->ssl); + peercert = mbedtls_ssl_get_peer_cert(&backend->ssl); if(peercert && data->set.verbose) { const size_t bufsize = 16384; @@ -661,7 +675,7 @@ mbed_connect_step2(struct connectdata *conn, #ifdef HAS_ALPN if(conn->bits.tls_enable_alpn) { - const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&BACKEND->ssl); + const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl); if(next_protocol) { infof(data, "ALPN, server accepted to use %s\n", next_protocol); @@ -698,6 +712,7 @@ mbed_connect_step3(struct connectdata *conn, { CURLcode retcode = CURLE_OK; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; struct Curl_easy *data = conn->data; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -713,7 +728,7 @@ mbed_connect_step3(struct connectdata *conn, mbedtls_ssl_session_init(our_ssl_sessionid); - ret = mbedtls_ssl_get_session(&BACKEND->ssl, our_ssl_sessionid); + ret = mbedtls_ssl_get_session(&backend->ssl, our_ssl_sessionid); if(ret) { if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED) mbedtls_ssl_session_free(our_ssl_sessionid); @@ -747,9 +762,10 @@ static ssize_t mbed_send(struct connectdata *conn, int sockindex, CURLcode *curlcode) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; int ret = -1; - ret = mbedtls_ssl_write(&BACKEND->ssl, + ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); if(ret < 0) { @@ -769,15 +785,16 @@ static void Curl_mbedtls_close_all(struct Curl_easy *data) static void Curl_mbedtls_close(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - mbedtls_pk_free(&BACKEND->pk); - mbedtls_x509_crt_free(&BACKEND->clicert); - mbedtls_x509_crt_free(&BACKEND->cacert); - mbedtls_x509_crl_free(&BACKEND->crl); - mbedtls_ssl_config_free(&BACKEND->config); - mbedtls_ssl_free(&BACKEND->ssl); - mbedtls_ctr_drbg_free(&BACKEND->ctr_drbg); + struct ssl_backend_data *backend = connssl->backend; + mbedtls_pk_free(&backend->pk); + mbedtls_x509_crt_free(&backend->clicert); + mbedtls_x509_crt_free(&backend->cacert); + mbedtls_x509_crl_free(&backend->crl); + mbedtls_ssl_config_free(&backend->config); + mbedtls_ssl_free(&backend->ssl); + mbedtls_ctr_drbg_free(&backend->ctr_drbg); #ifndef THREADING_SUPPORT - mbedtls_entropy_free(&BACKEND->entropy); + mbedtls_entropy_free(&backend->entropy); #endif /* THREADING_SUPPORT */ } @@ -786,11 +803,12 @@ static ssize_t mbed_recv(struct connectdata *conn, int num, CURLcode *curlcode) { struct ssl_connect_data *connssl = &conn->ssl[num]; + struct ssl_backend_data *backend = connssl->backend; int ret = -1; ssize_t len = -1; memset(buf, 0, buffersize); - ret = mbedtls_ssl_read(&BACKEND->ssl, (unsigned char *)buf, + ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, buffersize); if(ret <= 0) { @@ -884,7 +902,7 @@ mbed_connect_common(struct connectdata *conn, struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; - long timeout_ms; + timediff_t timeout_ms; int what; /* check if the connection has already been established */ @@ -1014,19 +1032,20 @@ static CURLcode Curl_mbedtls_connect(struct connectdata *conn, int sockindex) */ static int Curl_mbedtls_init(void) { - return Curl_polarsslthreadlock_thread_setup(); + return Curl_mbedtlsthreadlock_thread_setup(); } static void Curl_mbedtls_cleanup(void) { - (void)Curl_polarsslthreadlock_thread_cleanup(); + (void)Curl_mbedtlsthreadlock_thread_cleanup(); } static bool Curl_mbedtls_data_pending(const struct connectdata *conn, int sockindex) { const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - return mbedtls_ssl_get_bytes_avail(&BACKEND->ssl) != 0; + struct ssl_backend_data *backend = connssl->backend; + return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0; } static CURLcode Curl_mbedtls_sha256sum(const unsigned char *input, @@ -1048,8 +1067,9 @@ static CURLcode Curl_mbedtls_sha256sum(const unsigned char *input, static void *Curl_mbedtls_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { + struct ssl_backend_data *backend = connssl->backend; (void)info; - return &BACKEND->ssl; + return &backend->ssl; } const struct Curl_ssl Curl_ssl_mbedtls = { diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.h b/Utilities/cmcurl/lib/vtls/mbedtls.h index 4a938605bdb..0cc64b3991d 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.h +++ b/Utilities/cmcurl/lib/vtls/mbedtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2019, Daniel Stenberg, , et al. * Copyright (C) 2010, Hoi-Ho Chan, * * This software is licensed as described in the file COPYING, which diff --git a/Utilities/cmcurl/lib/vtls/polarssl_threadlock.c b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c similarity index 76% rename from Utilities/cmcurl/lib/vtls/polarssl_threadlock.c rename to Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c index 27c94b11e22..4d672f106e0 100644 --- a/Utilities/cmcurl/lib/vtls/polarssl_threadlock.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2013-2017, Daniel Stenberg, , et al. + * Copyright (C) 2013 - 2020, Daniel Stenberg, , et al. * Copyright (C) 2010, 2011, Hoi-Ho Chan, * * This software is licensed as described in the file COPYING, which @@ -22,19 +22,19 @@ ***************************************************************************/ #include "curl_setup.h" -#if (defined(USE_POLARSSL) || defined(USE_MBEDTLS)) && \ - ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ - (defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H))) +#if defined(USE_MBEDTLS) && \ + ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ + (defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H))) #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) # include -# define POLARSSL_MUTEX_T pthread_mutex_t +# define MBEDTLS_MUTEX_T pthread_mutex_t #elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) # include -# define POLARSSL_MUTEX_T HANDLE +# define MBEDTLS_MUTEX_T HANDLE #endif -#include "polarssl_threadlock.h" +#include "mbedtls_threadlock.h" #include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -43,14 +43,14 @@ /* number of thread locks */ #define NUMT 2 -/* This array will store all of the mutexes available to PolarSSL. */ -static POLARSSL_MUTEX_T *mutex_buf = NULL; +/* This array will store all of the mutexes available to Mbedtls. */ +static MBEDTLS_MUTEX_T *mutex_buf = NULL; -int Curl_polarsslthreadlock_thread_setup(void) +int Curl_mbedtlsthreadlock_thread_setup(void) { int i; - mutex_buf = calloc(NUMT * sizeof(POLARSSL_MUTEX_T), 1); + mutex_buf = calloc(NUMT * sizeof(MBEDTLS_MUTEX_T), 1); if(!mutex_buf) return 0; /* error, no number of threads defined */ @@ -70,7 +70,7 @@ int Curl_polarsslthreadlock_thread_setup(void) return 1; /* OK */ } -int Curl_polarsslthreadlock_thread_cleanup(void) +int Curl_mbedtlsthreadlock_thread_cleanup(void) { int i; @@ -95,7 +95,7 @@ int Curl_polarsslthreadlock_thread_cleanup(void) return 1; /* OK */ } -int Curl_polarsslthreadlock_lock_function(int n) +int Curl_mbedtlsthreadlock_lock_function(int n) { if(n < NUMT) { int ret; @@ -103,14 +103,14 @@ int Curl_polarsslthreadlock_lock_function(int n) ret = pthread_mutex_lock(&mutex_buf[n]); if(ret) { DEBUGF(fprintf(stderr, - "Error: polarsslthreadlock_lock_function failed\n")); + "Error: mbedtlsthreadlock_lock_function failed\n")); return 0; /* pthread_mutex_lock failed */ } #elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) ret = (WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED?1:0); if(ret) { DEBUGF(fprintf(stderr, - "Error: polarsslthreadlock_lock_function failed\n")); + "Error: mbedtlsthreadlock_lock_function failed\n")); return 0; /* pthread_mutex_lock failed */ } #endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ @@ -118,7 +118,7 @@ int Curl_polarsslthreadlock_lock_function(int n) return 1; /* OK */ } -int Curl_polarsslthreadlock_unlock_function(int n) +int Curl_mbedtlsthreadlock_unlock_function(int n) { if(n < NUMT) { int ret; @@ -126,14 +126,14 @@ int Curl_polarsslthreadlock_unlock_function(int n) ret = pthread_mutex_unlock(&mutex_buf[n]); if(ret) { DEBUGF(fprintf(stderr, - "Error: polarsslthreadlock_unlock_function failed\n")); + "Error: mbedtlsthreadlock_unlock_function failed\n")); return 0; /* pthread_mutex_unlock failed */ } #elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) ret = ReleaseMutex(mutex_buf[n]); if(!ret) { DEBUGF(fprintf(stderr, - "Error: polarsslthreadlock_unlock_function failed\n")); + "Error: mbedtlsthreadlock_unlock_function failed\n")); return 0; /* pthread_mutex_lock failed */ } #endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ @@ -141,4 +141,4 @@ int Curl_polarsslthreadlock_unlock_function(int n) return 1; /* OK */ } -#endif /* USE_POLARSSL || USE_MBEDTLS */ +#endif /* USE_MBEDTLS */ diff --git a/Utilities/cmcurl/lib/vtls/polarssl_threadlock.h b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h similarity index 64% rename from Utilities/cmcurl/lib/vtls/polarssl_threadlock.h rename to Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h index 122647528d5..96a787d1a24 100644 --- a/Utilities/cmcurl/lib/vtls/polarssl_threadlock.h +++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_POLARSSL_THREADLOCK_H -#define HEADER_CURL_POLARSSL_THREADLOCK_H +#ifndef HEADER_CURL_MBEDTLS_THREADLOCK_H +#define HEADER_CURL_MBEDTLS_THREADLOCK_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2013-2015, Daniel Stenberg, , et al. + * Copyright (C) 2013 - 2020, Daniel Stenberg, , et al. * Copyright (C) 2010, Hoi-Ho Chan, * * This software is licensed as described in the file COPYING, which @@ -24,25 +24,25 @@ ***************************************************************************/ #include "curl_setup.h" -#if (defined USE_POLARSSL) || (defined USE_MBEDTLS) +#ifdef USE_MBEDTLS #if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ (defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)) -int Curl_polarsslthreadlock_thread_setup(void); -int Curl_polarsslthreadlock_thread_cleanup(void); -int Curl_polarsslthreadlock_lock_function(int n); -int Curl_polarsslthreadlock_unlock_function(int n); +int Curl_mbedtlsthreadlock_thread_setup(void); +int Curl_mbedtlsthreadlock_thread_cleanup(void); +int Curl_mbedtlsthreadlock_lock_function(int n); +int Curl_mbedtlsthreadlock_unlock_function(int n); #else -#define Curl_polarsslthreadlock_thread_setup() 1 -#define Curl_polarsslthreadlock_thread_cleanup() 1 -#define Curl_polarsslthreadlock_lock_function(x) 1 -#define Curl_polarsslthreadlock_unlock_function(x) 1 +#define Curl_mbedtlsthreadlock_thread_setup() 1 +#define Curl_mbedtlsthreadlock_thread_cleanup() 1 +#define Curl_mbedtlsthreadlock_lock_function(x) 1 +#define Curl_mbedtlsthreadlock_unlock_function(x) 1 #endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */ -#endif /* USE_POLARSSL */ +#endif /* USE_MBEDTLS */ -#endif /* HEADER_CURL_POLARSSL_THREADLOCK_H */ +#endif /* HEADER_CURL_MBEDTLS_THREADLOCK_H */ diff --git a/Utilities/cmcurl/lib/vtls/mesalink.c b/Utilities/cmcurl/lib/vtls/mesalink.c index 718c282ee56..7132bdfd22a 100644 --- a/Utilities/cmcurl/lib/vtls/mesalink.c +++ b/Utilities/cmcurl/lib/vtls/mesalink.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2017 - 2018, Yiming Jing, - * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -73,6 +73,17 @@ struct ssl_backend_data static Curl_recv mesalink_recv; static Curl_send mesalink_send; +static int do_file_type(const char *type) +{ + if(!type || !type[0]) + return SSL_FILETYPE_PEM; + if(strcasecompare(type, "PEM")) + return SSL_FILETYPE_PEM; + if(strcasecompare(type, "DER")) + return SSL_FILETYPE_ASN1; + return -1; +} + /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. @@ -83,9 +94,6 @@ mesalink_connect_step1(struct connectdata *conn, int sockindex) char *ciphers; struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile); - const char *const ssl_capath = SSL_CONN_CONFIG(CApath); struct in_addr addr4; #ifdef ENABLE_IPV6 struct in6_addr addr6; @@ -142,21 +150,25 @@ mesalink_connect_step1(struct connectdata *conn, int sockindex) } SSL_CTX_set_verify( - BACKEND->ctx, verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); + BACKEND->ctx, SSL_CONN_CONFIG(verifypeer) ? + SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); - if(ssl_cafile || ssl_capath) { - if(!SSL_CTX_load_verify_locations(BACKEND->ctx, ssl_cafile, ssl_capath)) { - if(verifypeer) { + if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath)) { + if(!SSL_CTX_load_verify_locations(BACKEND->ctx, SSL_CONN_CONFIG(CAfile), + SSL_CONN_CONFIG(CApath))) { + if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); + SSL_CONN_CONFIG(CAfile) ? + SSL_CONN_CONFIG(CAfile) : "none", + SSL_CONN_CONFIG(CApath) ? + SSL_CONN_CONFIG(CApath) : "none"); return CURLE_SSL_CACERT_BADFILE; } infof(data, - "error setting certificate verify locations," - " continuing anyway:\n"); + "error setting certificate verify locations," + " continuing anyway:\n"); } else { infof(data, "successfully set certificate verify locations:\n"); @@ -164,8 +176,32 @@ mesalink_connect_step1(struct connectdata *conn, int sockindex) infof(data, " CAfile: %s\n" " CApath: %s\n", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); + SSL_CONN_CONFIG(CAfile)? + SSL_CONN_CONFIG(CAfile): "none", + SSL_CONN_CONFIG(CApath)? + SSL_CONN_CONFIG(CApath): "none"); + } + + if(SSL_SET_OPTION(cert) && SSL_SET_OPTION(key)) { + int file_type = do_file_type(SSL_SET_OPTION(cert_type)); + + if(SSL_CTX_use_certificate_chain_file(BACKEND->ctx, SSL_SET_OPTION(cert), + file_type) != 1) { + failf(data, "unable to use client certificate (no key or wrong pass" + " phrase?)"); + return CURLE_SSL_CONNECT_ERROR; + } + + file_type = do_file_type(SSL_SET_OPTION(key_type)); + if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key), + file_type) != 1) { + failf(data, "unable to set private key"); + return CURLE_SSL_CONNECT_ERROR; + } + infof(data, + "client cert: %s\n", + SSL_CONN_CONFIG(clientcert)? + SSL_CONN_CONFIG(clientcert): "none"); } ciphers = SSL_CONN_CONFIG(cipher_list); @@ -265,7 +301,6 @@ mesalink_connect_step2(struct connectdata *conn, int sockindex) ret = SSL_connect(BACKEND->handle); if(ret != SSL_SUCCESS) { - char error_buffer[MESALINK_MAX_ERROR_SZ]; int detail = SSL_get_error(BACKEND->handle, ret); if(SSL_ERROR_WANT_CONNECT == detail || SSL_ERROR_WANT_READ == detail) { @@ -273,6 +308,7 @@ mesalink_connect_step2(struct connectdata *conn, int sockindex) return CURLE_OK; } else { + char error_buffer[MESALINK_MAX_ERROR_SZ]; failf(data, "SSL_connect failed with error %d: %s", detail, @@ -458,7 +494,7 @@ mesalink_connect_common(struct connectdata *conn, int sockindex, struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; - time_t timeout_ms; + timediff_t timeout_ms; int what; /* check if the connection has already been established */ @@ -506,8 +542,8 @@ mesalink_connect_common(struct connectdata *conn, int sockindex, ? sockfd : CURL_SOCKET_BAD; - what = Curl_socket_check( - readfd, CURL_SOCKET_BAD, writefd, nonblocking ? 0 : timeout_ms); + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + nonblocking ? 0 : timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c index 491def106d1..fca29261381 100644 --- a/Utilities/cmcurl/lib/vtls/nss.c +++ b/Utilities/cmcurl/lib/vtls/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -87,8 +87,6 @@ struct ssl_backend_data { PK11GenericObject *obj_clicert; }; -#define BACKEND connssl->backend - static PRLock *nss_initlock = NULL; static PRLock *nss_crllock = NULL; static PRLock *nss_findslot_lock = NULL; @@ -103,22 +101,22 @@ struct ptr_list_wrap { struct curl_llist_element node; }; -typedef struct { +struct cipher_s { const char *name; int num; -} cipher_s; +}; #define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \ CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++); \ ptr->type = (_type); \ ptr->pValue = (_val); \ ptr->ulValueLen = (_len); \ -} WHILE_FALSE +} while(0) #define CERT_NewTempCertificate __CERT_NewTempCertificate #define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0]) -static const cipher_s cipherlist[] = { +static const struct cipher_s cipherlist[] = { /* SSL2 cipher suites */ {"rc4", SSL_EN_RC4_128_WITH_MD5}, {"rc4-md5", SSL_EN_RC4_128_WITH_MD5}, @@ -216,11 +214,19 @@ static const cipher_s cipherlist[] = { {"dhe_rsa_chacha20_poly1305_sha_256", TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, #endif +#ifdef TLS_AES_256_GCM_SHA384 + {"aes_128_gcm_sha_256", TLS_AES_128_GCM_SHA256}, + {"aes_256_gcm_sha_384", TLS_AES_256_GCM_SHA384}, + {"chacha20_poly1305_sha_256", TLS_CHACHA20_POLY1305_SHA256}, +#endif }; -#ifdef WIN32 +#if defined(WIN32) static const char *pem_library = "nsspem.dll"; static const char *trust_library = "nssckbi.dll"; +#elif defined(__APPLE__) +static const char *pem_library = "libnsspem.dylib"; +static const char *trust_library = "libnssckbi.dylib"; #else static const char *pem_library = "libnsspem.so"; static const char *trust_library = "libnssckbi.so"; @@ -454,6 +460,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *connssl, const int slot_id = (cacert) ? 0 : 1; char *slot_name = aprintf("PEM Token #%d", slot_id); + struct ssl_backend_data *backend = connssl->backend; if(!slot_name) return CURLE_OUT_OF_MEMORY; @@ -487,14 +494,14 @@ static CURLcode nss_create_object(struct ssl_connect_data *connssl, if(!obj) return result; - if(insert_wrapped_ptr(&BACKEND->obj_list, obj) != CURLE_OK) { + if(insert_wrapped_ptr(&backend->obj_list, obj) != CURLE_OK) { PK11_DestroyGenericObject(obj); return CURLE_OUT_OF_MEMORY; } if(!cacert && CKO_CERTIFICATE == obj_class) /* store reference to a client certificate */ - BACKEND->obj_clicert = obj; + backend->obj_clicert = obj; return CURLE_OK; } @@ -573,17 +580,19 @@ static CURLcode nss_cache_crl(SECItem *crl_der) /* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */ PR_Lock(nss_crllock); - /* store the CRL item so that we can free it in Curl_nss_cleanup() */ - if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) { + if(SECSuccess != CERT_CacheCRL(db, crl_der)) { + /* unable to cache CRL */ SECITEM_FreeItem(crl_der, PR_TRUE); PR_Unlock(nss_crllock); - return CURLE_OUT_OF_MEMORY; + return CURLE_SSL_CRL_BADFILE; } - if(SECSuccess != CERT_CacheCRL(db, crl_der)) { - /* unable to cache CRL */ + /* store the CRL item so that we can free it in Curl_nss_cleanup() */ + if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) { + if(SECSuccess == CERT_UncacheCRL(db, crl_der)) + SECITEM_FreeItem(crl_der, PR_TRUE); PR_Unlock(nss_crllock); - return CURLE_SSL_CRL_BADFILE; + return CURLE_OUT_OF_MEMORY; } /* we need to clear session cache, so that the CRL could take effect */ @@ -681,7 +690,10 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex, tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0); if(tmp) PK11_FreeSlot(tmp); - PK11_IsPresent(slot); + if(!PK11_IsPresent(slot)) { + PK11_FreeSlot(slot); + return CURLE_SSL_CERTPROBLEM; + } status = PK11_Authenticate(slot, PR_TRUE, SSL_SET_OPTION(key_passwd)); PK11_FreeSlot(slot); @@ -1071,7 +1083,8 @@ static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, const char *pinnedpubkey) { CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - struct Curl_easy *data = BACKEND->data; + struct ssl_backend_data *backend = connssl->backend; + struct Curl_easy *data = backend->data; CERTCertificate *cert; if(!pinnedpubkey) @@ -1079,7 +1092,7 @@ static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, return CURLE_OK; /* get peer certificate */ - cert = SSL_PeerCertificate(BACKEND->handle); + cert = SSL_PeerCertificate(backend->handle); if(cert) { /* extract public key from peer certificate */ SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert); @@ -1123,11 +1136,12 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, struct SECKEYPrivateKeyStr **pRetKey) { struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; - struct Curl_easy *data = BACKEND->data; - const char *nickname = BACKEND->client_nickname; + struct ssl_backend_data *backend = connssl->backend; + struct Curl_easy *data = backend->data; + const char *nickname = backend->client_nickname; static const char pem_slotname[] = "PEM Token #1"; - if(BACKEND->obj_clicert) { + if(backend->obj_clicert) { /* use the cert/key provided by PEM reader */ SECItem cert_der = { 0, NULL, 0 }; void *proto_win = SSL_RevealPinArg(sock); @@ -1140,7 +1154,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, return SECFailure; } - if(PK11_ReadRawAttribute(PK11_TypeGeneric, BACKEND->obj_clicert, CKA_VALUE, + if(PK11_ReadRawAttribute(PK11_TypeGeneric, backend->obj_clicert, CKA_VALUE, &cert_der) != SECSuccess) { failf(data, "NSS: CKA_VALUE not found in PK11 generic object"); PK11_FreeSlot(slot); @@ -1416,7 +1430,7 @@ static int Curl_nss_init(void) { /* curl_global_init() is not thread-safe so this test is ok */ if(nss_initlock == NULL) { - PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256); + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); nss_initlock = PR_NewLock(); nss_crllock = PR_NewLock(); nss_findslot_lock = PR_NewLock(); @@ -1490,11 +1504,12 @@ static void Curl_nss_cleanup(void) static int Curl_nss_check_cxn(struct connectdata *conn) { struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; + struct ssl_backend_data *backend = connssl->backend; int rc; char buf; rc = - PR_Recv(BACKEND->handle, (void *)&buf, 1, PR_MSG_PEEK, + PR_Recv(backend->handle, (void *)&buf, 1, PR_MSG_PEEK, PR_SecondsToInterval(1)); if(rc > 0) return 1; /* connection still in place */ @@ -1508,26 +1523,27 @@ static int Curl_nss_check_cxn(struct connectdata *conn) static void nss_close(struct ssl_connect_data *connssl) { /* before the cleanup, check whether we are using a client certificate */ - const bool client_cert = (BACKEND->client_nickname != NULL) - || (BACKEND->obj_clicert != NULL); + struct ssl_backend_data *backend = connssl->backend; + const bool client_cert = (backend->client_nickname != NULL) + || (backend->obj_clicert != NULL); - free(BACKEND->client_nickname); - BACKEND->client_nickname = NULL; + free(backend->client_nickname); + backend->client_nickname = NULL; /* destroy all NSS objects in order to avoid failure of NSS shutdown */ - Curl_llist_destroy(&BACKEND->obj_list, NULL); - BACKEND->obj_clicert = NULL; + Curl_llist_destroy(&backend->obj_list, NULL); + backend->obj_clicert = NULL; - if(BACKEND->handle) { + if(backend->handle) { if(client_cert) /* A server might require different authentication based on the * particular path being requested by the client. To support this * scenario, we must ensure that a connection will never reuse the * authentication data from a previous connection. */ - SSL_InvalidateSession(BACKEND->handle); + SSL_InvalidateSession(backend->handle); - PR_Close(BACKEND->handle); - BACKEND->handle = NULL; + PR_Close(backend->handle); + backend->handle = NULL; } } @@ -1538,15 +1554,16 @@ static void Curl_nss_close(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_connect_data *connssl_proxy = &conn->proxy_ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; - if(BACKEND->handle || connssl_proxy->backend->handle) { + if(backend->handle || connssl_proxy->backend->handle) { /* NSS closes the socket we previously handed to it, so we must mark it as closed to avoid double close */ fake_sclose(conn->sock[sockindex]); conn->sock[sockindex] = CURL_SOCKET_BAD; } - if(BACKEND->handle) + if(backend->handle) /* nss_close(connssl) will transitively close also connssl_proxy->backend->handle if both are used. Clear it to avoid a double close leading to crash. */ @@ -1721,20 +1738,16 @@ static CURLcode nss_init_sslver(SSLVersionRange *sslver, CURLcode result; const long min = SSL_CONN_CONFIG(version); const long max = SSL_CONN_CONFIG(version_max); - - /* map CURL_SSLVERSION_DEFAULT to NSS default */ - if(min == CURL_SSLVERSION_DEFAULT || max == CURL_SSLVERSION_MAX_DEFAULT) { - /* map CURL_SSLVERSION_DEFAULT to NSS default */ - if(SSL_VersionRangeGetDefault(ssl_variant_stream, sslver) != SECSuccess) - return CURLE_SSL_CONNECT_ERROR; - /* ... but make sure we use at least TLSv1.0 according to libcurl API */ - if(sslver->min < SSL_LIBRARY_VERSION_TLS_1_0) - sslver->min = SSL_LIBRARY_VERSION_TLS_1_0; - } + SSLVersionRange vrange; switch(min) { case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_DEFAULT: + /* Bump our minimum TLS version if NSS has stricter requirements. */ + if(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange) != SECSuccess) + return CURLE_SSL_CONNECT_ERROR; + if(sslver->min < vrange.min) + sslver->min = vrange.min; break; default: result = nss_sslver_from_curl(&sslver->min, min); @@ -1764,6 +1777,7 @@ static CURLcode nss_fail_connect(struct ssl_connect_data *connssl, CURLcode curlerr) { PRErrorCode err = 0; + struct ssl_backend_data *backend = connssl->backend; if(is_nss_error(curlerr)) { /* read NSPR error code */ @@ -1779,7 +1793,7 @@ static CURLcode nss_fail_connect(struct ssl_connect_data *connssl, } /* cleanup on connection failure */ - Curl_llist_destroy(&BACKEND->obj_list, NULL); + Curl_llist_destroy(&backend->obj_list, NULL); return curlerr; } @@ -1790,10 +1804,11 @@ static CURLcode nss_set_blocking(struct ssl_connect_data *connssl, bool blocking) { static PRSocketOptionData sock_opt; + struct ssl_backend_data *backend = connssl->backend; sock_opt.option = PR_SockOpt_Nonblocking; sock_opt.value.non_blocking = !blocking; - if(PR_SetSocketOption(BACKEND->handle, &sock_opt) != PR_SUCCESS) + if(PR_SetSocketOption(backend->handle, &sock_opt) != PR_SUCCESS) return nss_fail_connect(connssl, data, CURLE_SSL_CONNECT_ERROR); return CURLE_OK; @@ -1809,6 +1824,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) struct Curl_easy *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; CURLcode result; bool second_layer = FALSE; SSLVersionRange sslver_supported; @@ -1826,10 +1842,10 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) #endif }; - BACKEND->data = data; + backend->data = data; /* list of all NSS objects we need to destroy in Curl_nss_close() */ - Curl_llist_init(&BACKEND->obj_list, nss_destroy_object); + Curl_llist_init(&backend->obj_list, nss_destroy_object); PR_Lock(nss_initlock); result = nss_init(conn->data); @@ -1951,7 +1967,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) char *nickname = dup_nickname(data, SSL_SET_OPTION(cert)); if(nickname) { /* we are not going to use libnsspem.so to read the client cert */ - BACKEND->obj_clicert = NULL; + backend->obj_clicert = NULL; } else { CURLcode rv = cert_stuff(conn, sockindex, SSL_SET_OPTION(cert), @@ -1964,10 +1980,10 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) } /* store the nickname for SelectClientCert() called during handshake */ - BACKEND->client_nickname = nickname; + backend->client_nickname = nickname; } else - BACKEND->client_nickname = NULL; + backend->client_nickname = NULL; if(SSL_GetClientAuthDataHook(model, SelectClientCert, (void *)connssl) != SECSuccess) { @@ -2008,8 +2024,8 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) } /* import our model socket onto the current I/O stack */ - BACKEND->handle = SSL_ImportFD(model, nspr_io); - if(!BACKEND->handle) { + backend->handle = SSL_ImportFD(model, nspr_io); + if(!backend->handle) { if(!second_layer) PR_Close(nspr_io); goto error; @@ -2020,36 +2036,36 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) /* This is the password associated with the cert that we're using */ if(SSL_SET_OPTION(key_passwd)) { - SSL_SetPKCS11PinArg(BACKEND->handle, SSL_SET_OPTION(key_passwd)); + SSL_SetPKCS11PinArg(backend->handle, SSL_SET_OPTION(key_passwd)); } #ifdef SSL_ENABLE_OCSP_STAPLING if(SSL_CONN_CONFIG(verifystatus)) { - if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) + if(SSL_OptionSet(backend->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) != SECSuccess) goto error; } #endif #ifdef SSL_ENABLE_NPN - if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_NPN, conn->bits.tls_enable_npn + if(SSL_OptionSet(backend->handle, SSL_ENABLE_NPN, conn->bits.tls_enable_npn ? PR_TRUE : PR_FALSE) != SECSuccess) goto error; #endif #ifdef SSL_ENABLE_ALPN - if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn + if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn ? PR_TRUE : PR_FALSE) != SECSuccess) goto error; #endif #if NSSVERNUM >= 0x030f04 /* 3.15.4 */ if(data->set.ssl.falsestart) { - if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_FALSE_START, PR_TRUE) + if(SSL_OptionSet(backend->handle, SSL_ENABLE_FALSE_START, PR_TRUE) != SECSuccess) goto error; - if(SSL_SetCanFalseStartCallback(BACKEND->handle, CanFalseStartCallback, + if(SSL_SetCanFalseStartCallback(backend->handle, CanFalseStartCallback, conn) != SECSuccess) goto error; } @@ -2073,24 +2089,24 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); cur += ALPN_HTTP_1_1_LENGTH; - if(SSL_SetNextProtoNego(BACKEND->handle, protocols, cur) != SECSuccess) + if(SSL_SetNextProtoNego(backend->handle, protocols, cur) != SECSuccess) goto error; } #endif /* Force handshake on next I/O */ - if(SSL_ResetHandshake(BACKEND->handle, /* asServer */ PR_FALSE) + if(SSL_ResetHandshake(backend->handle, /* asServer */ PR_FALSE) != SECSuccess) goto error; /* propagate hostname to the TLS layer */ - if(SSL_SetURL(BACKEND->handle, SSL_IS_PROXY() ? conn->http_proxy.host.name : + if(SSL_SetURL(backend->handle, SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name) != SECSuccess) goto error; /* prevent NSS from re-using the session for a different hostname */ - if(SSL_SetSockPeerID(BACKEND->handle, SSL_IS_PROXY() ? + if(SSL_SetSockPeerID(backend->handle, SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name) != SECSuccess) goto error; @@ -2107,6 +2123,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; struct Curl_easy *data = conn->data; CURLcode result = CURLE_SSL_CONNECT_ERROR; PRUint32 timeout; @@ -2118,7 +2135,7 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) /* check timeout situation */ - const time_t time_left = Curl_timeleft(data, NULL, TRUE); + const timediff_t time_left = Curl_timeleft(data, NULL, TRUE); if(time_left < 0) { failf(data, "timed out before SSL handshake"); result = CURLE_OPERATION_TIMEDOUT; @@ -2127,7 +2144,7 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) /* Force the handshake now */ timeout = PR_MillisecondsToInterval((PRUint32) time_left); - if(SSL_ForceHandshakeWithTimeout(BACKEND->handle, timeout) != SECSuccess) { + if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) { if(PR_GetError() == PR_WOULD_BLOCK_ERROR) /* blocking direction is updated by nss_update_connecting_state() */ return CURLE_AGAIN; @@ -2138,7 +2155,7 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) goto error; } - result = display_conn_info(conn, BACKEND->handle); + result = display_conn_info(conn, backend->handle); if(result) goto error; @@ -2147,7 +2164,7 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) char *nickname = dup_nickname(data, SSL_SET_OPTION(issuercert)); if(nickname) { /* we support only nicknames in case of issuercert for now */ - ret = check_issuer_cert(BACKEND->handle, nickname); + ret = check_issuer_cert(backend->handle, nickname); free(nickname); } @@ -2251,13 +2268,14 @@ static ssize_t nss_send(struct connectdata *conn, /* connection data */ CURLcode *curlcode) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; ssize_t rc; /* The SelectClientCert() hook uses this for infof() and failf() but the handle stored in nss_setup_connect() could have already been freed. */ - BACKEND->data = conn->data; + backend->data = conn->data; - rc = PR_Send(BACKEND->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT); + rc = PR_Send(backend->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT); if(rc < 0) { PRInt32 err = PR_GetError(); if(err == PR_WOULD_BLOCK_ERROR) @@ -2288,13 +2306,14 @@ static ssize_t nss_recv(struct connectdata *conn, /* connection data */ CURLcode *curlcode) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; ssize_t nread; /* The SelectClientCert() hook uses this for infof() and failf() but the handle stored in nss_setup_connect() could have already been freed. */ - BACKEND->data = conn->data; + backend->data = conn->data; - nread = PR_Recv(BACKEND->handle, buf, (int)buffersize, 0, + nread = PR_Recv(backend->handle, buf, (int)buffersize, 0, PR_INTERVAL_NO_WAIT); if(nread < 0) { /* failed SSL read */ @@ -2355,6 +2374,9 @@ static CURLcode Curl_nss_md5sum(unsigned char *tmp, /* input */ PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5); unsigned int MD5out; + if(!MD5pw) + return CURLE_NOT_BUILT_IN; + PK11_DigestOp(MD5pw, tmp, curlx_uztoui(tmplen)); PK11_DigestFinal(MD5pw, md5sum, &MD5out, curlx_uztoui(md5len)); PK11_DestroyContext(MD5pw, PR_TRUE); @@ -2370,6 +2392,9 @@ static CURLcode Curl_nss_sha256sum(const unsigned char *tmp, /* input */ PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256); unsigned int SHA256out; + if(!SHA256pw) + return CURLE_NOT_BUILT_IN; + PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen)); PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len)); PK11_DestroyContext(SHA256pw, PR_TRUE); @@ -2398,8 +2423,9 @@ static bool Curl_nss_false_start(void) static void *Curl_nss_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { + struct ssl_backend_data *backend = connssl->backend; (void)info; - return BACKEND->handle; + return backend->handle; } const struct Curl_ssl Curl_ssl_nss = { diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 85e9be61611..2e9f900da3f 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,17 +25,17 @@ * but vtls.c should ever call or use these functions. */ -/* - * The original SSLeay-using code for curl was written by Linas Vepstas and - * Sampo Kellomaki 1998. - */ - #include "curl_setup.h" #ifdef USE_OPENSSL #include +/* Wincrypt must be included before anything that could include OpenSSL. */ +#if defined(USE_WIN32_CRYPTO) +#include +#endif + #include "urldata.h" #include "sendf.h" #include "formdata.h" /* for the boundary function */ @@ -46,10 +46,13 @@ #include "slist.h" #include "select.h" #include "vtls.h" +#include "keylog.h" #include "strcase.h" #include "hostcheck.h" #include "multiif.h" +#include "strerror.h" #include "curl_printf.h" + #include #include #include @@ -75,7 +78,7 @@ #endif #if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) && /* 0.9.7 or later */ \ - !defined(OPENSSL_NO_ENGINE) + !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE) #define USE_OPENSSL_ENGINE #include #endif @@ -146,16 +149,16 @@ #endif #endif -#ifdef LIBRESSL_VERSION_NUMBER -#define OpenSSL_version_num() LIBRESSL_VERSION_NUMBER -#endif - #if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* 1.0.2 or later */ \ !(defined(LIBRESSL_VERSION_NUMBER) && \ LIBRESSL_VERSION_NUMBER < 0x20700000L) #define HAVE_X509_GET0_SIGNATURE 1 #endif +#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */ +#define HAVE_SSL_GET_SHUTDOWN 1 +#endif + #if OPENSSL_VERSION_NUMBER >= 0x10002003L && \ OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \ !defined(OPENSSL_NO_COMP) @@ -211,29 +214,17 @@ "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH" #endif -#define ENABLE_SSLKEYLOGFILE - -#ifdef ENABLE_SSLKEYLOGFILE -typedef struct ssl_tap_state { - int master_key_length; - unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; - unsigned char client_random[SSL3_RANDOM_SIZE]; -} ssl_tap_state_t; -#endif /* ENABLE_SSLKEYLOGFILE */ - struct ssl_backend_data { /* these ones requires specific SSL-types */ SSL_CTX* ctx; SSL* handle; X509* server_cert; -#ifdef ENABLE_SSLKEYLOGFILE - /* tap_state holds the last seen master key if we're logging them */ - ssl_tap_state_t tap_state; +#ifndef HAVE_KEYLOG_CALLBACK + /* Set to true once a valid keylog entry has been created to avoid dupes. */ + bool keylog_done; #endif }; -#define BACKEND connssl->backend - /* * Number of bytes to read from the random number seed file. This must be * a finite value (because some entropy "files" like /dev/urandom have @@ -242,57 +233,27 @@ struct ssl_backend_data { */ #define RAND_LOAD_LENGTH 1024 -#ifdef ENABLE_SSLKEYLOGFILE -/* The fp for the open SSLKEYLOGFILE, or NULL if not open */ -static FILE *keylog_file_fp; - #ifdef HAVE_KEYLOG_CALLBACK static void ossl_keylog_callback(const SSL *ssl, const char *line) { (void)ssl; - /* Using fputs here instead of fprintf since libcurl's fprintf replacement - may not be thread-safe. */ - if(keylog_file_fp && line && *line) { - char stackbuf[256]; - char *buf; - size_t linelen = strlen(line); - - if(linelen <= sizeof(stackbuf) - 2) - buf = stackbuf; - else { - buf = malloc(linelen + 2); - if(!buf) - return; - } - memcpy(buf, line, linelen); - buf[linelen] = '\n'; - buf[linelen + 1] = '\0'; - - fputs(buf, keylog_file_fp); - if(buf != stackbuf) - free(buf); - } + Curl_tls_keylog_write_line(line); } #else -#define KEYLOG_PREFIX "CLIENT_RANDOM " -#define KEYLOG_PREFIX_LEN (sizeof(KEYLOG_PREFIX) - 1) /* - * tap_ssl_key is called by libcurl to make the CLIENT_RANDOMs if the OpenSSL - * being used doesn't have native support for doing that. + * ossl_log_tls12_secret is called by libcurl to make the CLIENT_RANDOMs if the + * OpenSSL being used doesn't have native support for doing that. */ -static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state) +static void +ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) { - const char *hex = "0123456789ABCDEF"; - int pos, i; - char line[KEYLOG_PREFIX_LEN + 2 * SSL3_RANDOM_SIZE + 1 + - 2 * SSL_MAX_MASTER_KEY_LENGTH + 1 + 1]; const SSL_SESSION *session = SSL_get_session(ssl); unsigned char client_random[SSL3_RANDOM_SIZE]; unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; int master_key_length = 0; - if(!session || !keylog_file_fp) + if(!session || *keylog_done) return; #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ @@ -311,44 +272,17 @@ static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state) } #endif + /* The handshake has not progressed sufficiently yet, or this is a TLS 1.3 + * session (when curl was built with older OpenSSL headers and running with + * newer OpenSSL runtime libraries). */ if(master_key_length <= 0) return; - /* Skip writing keys if there is no key or it did not change. */ - if(state->master_key_length == master_key_length && - !memcmp(state->master_key, master_key, master_key_length) && - !memcmp(state->client_random, client_random, SSL3_RANDOM_SIZE)) { - return; - } - - state->master_key_length = master_key_length; - memcpy(state->master_key, master_key, master_key_length); - memcpy(state->client_random, client_random, SSL3_RANDOM_SIZE); - - memcpy(line, KEYLOG_PREFIX, KEYLOG_PREFIX_LEN); - pos = KEYLOG_PREFIX_LEN; - - /* Client Random for SSLv3/TLS */ - for(i = 0; i < SSL3_RANDOM_SIZE; i++) { - line[pos++] = hex[client_random[i] >> 4]; - line[pos++] = hex[client_random[i] & 0xF]; - } - line[pos++] = ' '; - - /* Master Secret (size is at most SSL_MAX_MASTER_KEY_LENGTH) */ - for(i = 0; i < master_key_length; i++) { - line[pos++] = hex[master_key[i] >> 4]; - line[pos++] = hex[master_key[i] & 0xF]; - } - line[pos++] = '\n'; - line[pos] = '\0'; - - /* Using fputs here instead of fprintf since libcurl's fprintf replacement - may not be thread-safe. */ - fputs(line, keylog_file_fp); + *keylog_done = true; + Curl_tls_keylog_write("CLIENT_RANDOM", client_random, + master_key, master_key_length); } #endif /* !HAVE_KEYLOG_CALLBACK */ -#endif /* ENABLE_SSLKEYLOGFILE */ static const char *SSL_ERROR_to_str(int err) { @@ -392,7 +326,20 @@ static const char *SSL_ERROR_to_str(int err) */ static char *ossl_strerror(unsigned long error, char *buf, size_t size) { + if(size) + *buf = '\0'; + +#ifdef OPENSSL_IS_BORINGSSL + ERR_error_string_n((uint32_t)error, buf, size); +#else ERR_error_string_n(error, buf, size); +#endif + + if(size > 1 && !*buf) { + strncpy(buf, (error ? "Unknown error" : "No error"), size); + buf[size - 1] = '\0'; + } + return buf; } @@ -609,12 +556,136 @@ static bool is_pkcs11_uri(const char *string) static CURLcode Curl_ossl_set_engine(struct Curl_easy *data, const char *engine); +static int +SSL_CTX_use_certificate_bio(SSL_CTX *ctx, BIO *in, int type, + const char *key_passwd) +{ + int ret = 0; + X509 *x = NULL; + + if(type == SSL_FILETYPE_ASN1) { + /* j = ERR_R_ASN1_LIB; */ + x = d2i_X509_bio(in, NULL); + } + else if(type == SSL_FILETYPE_PEM) { + /* ERR_R_PEM_LIB; */ + x = PEM_read_bio_X509(in, NULL, + passwd_callback, (void *)key_passwd); + } + else { + ret = 0; + goto end; + } + + if(x == NULL) { + ret = 0; + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + end: + X509_free(x); + return ret; +} + +static int +SSL_CTX_use_PrivateKey_bio(SSL_CTX *ctx, BIO* in, int type, + const char *key_passwd) +{ + int ret = 0; + EVP_PKEY *pkey = NULL; + + if(type == SSL_FILETYPE_PEM) + pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback, + (void *)key_passwd); + else if(type == SSL_FILETYPE_ASN1) + pkey = d2i_PrivateKey_bio(in, NULL); + else { + ret = 0; + goto end; + } + if(pkey == NULL) { + ret = 0; + goto end; + } + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + EVP_PKEY_free(pkey); + end: + return ret; +} + +static int +SSL_CTX_use_certificate_chain_bio(SSL_CTX *ctx, BIO* in, + const char *key_passwd) +{ +/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */ +#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */ + int ret = 0; + X509 *x = NULL; + void *passwd_callback_userdata = (void *)key_passwd; + + ERR_clear_error(); + + x = PEM_read_bio_X509_AUX(in, NULL, + passwd_callback, (void *)key_passwd); + + if(x == NULL) { + ret = 0; + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + + if(ERR_peek_error() != 0) + ret = 0; + + if(ret) { + X509 *ca; + unsigned long err; + + if(!SSL_CTX_clear_chain_certs(ctx)) { + ret = 0; + goto end; + } + + while((ca = PEM_read_bio_X509(in, NULL, passwd_callback, + passwd_callback_userdata)) + != NULL) { + + if(!SSL_CTX_add0_chain_cert(ctx, ca)) { + X509_free(ca); + ret = 0; + goto end; + } + } + + err = ERR_peek_last_error(); + if((ERR_GET_LIB(err) == ERR_LIB_PEM) && + (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) + ERR_clear_error(); + else + ret = 0; + } + + end: + X509_free(x); + return ret; +#else + (void)ctx; /* unused */ + (void)in; /* unused */ + (void)key_passwd; /* unused */ + return 0; +#endif +} + static int cert_stuff(struct connectdata *conn, SSL_CTX* ctx, char *cert_file, + BIO *cert_bio, const char *cert_type, char *key_file, + BIO* key_bio, const char *key_type, char *key_passwd) { @@ -624,10 +695,11 @@ int cert_stuff(struct connectdata *conn, int file_type = do_file_type(cert_type); - if(cert_file || (file_type == SSL_FILETYPE_ENGINE)) { + if(cert_file || cert_bio || (file_type == SSL_FILETYPE_ENGINE)) { SSL *ssl; X509 *x509; int cert_done = 0; + int cert_use_result; if(key_passwd) { /* set the password in the callback userdata */ @@ -640,8 +712,10 @@ int cert_stuff(struct connectdata *conn, switch(file_type) { case SSL_FILETYPE_PEM: /* SSL_CTX_use_certificate_chain_file() only works on PEM files */ - if(SSL_CTX_use_certificate_chain_file(ctx, - cert_file) != 1) { + cert_use_result = cert_bio ? + SSL_CTX_use_certificate_chain_bio(ctx, cert_bio, key_passwd) : + SSL_CTX_use_certificate_chain_file(ctx, cert_file); + if(cert_use_result != 1) { failf(data, "could not load PEM client certificate, " OSSL_PACKAGE " error %s, " @@ -656,9 +730,12 @@ int cert_stuff(struct connectdata *conn, /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but we use the case above for PEM so this can only be performed with ASN1 files. */ - if(SSL_CTX_use_certificate_file(ctx, - cert_file, - file_type) != 1) { + + cert_use_result = cert_bio ? + SSL_CTX_use_certificate_bio(ctx, cert_bio, + file_type, key_passwd) : + SSL_CTX_use_certificate_file(ctx, cert_file, file_type); + if(cert_use_result != 1) { failf(data, "could not load ASN1 client certificate, " OSSL_PACKAGE " error %s, " @@ -738,27 +815,31 @@ int cert_stuff(struct connectdata *conn, PKCS12 *p12 = NULL; EVP_PKEY *pri; STACK_OF(X509) *ca = NULL; + if(!cert_bio) { + fp = BIO_new(BIO_s_file()); + if(fp == NULL) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + return 0; + } - fp = BIO_new(BIO_s_file()); - if(fp == NULL) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - return 0; + if(BIO_read_filename(fp, cert_file) <= 0) { + failf(data, "could not open PKCS12 file '%s'", cert_file); + BIO_free(fp); + return 0; + } } - if(BIO_read_filename(fp, cert_file) <= 0) { - failf(data, "could not open PKCS12 file '%s'", cert_file); + p12 = d2i_PKCS12_bio(cert_bio ? cert_bio : fp, NULL); + if(fp) BIO_free(fp); - return 0; - } - p12 = d2i_PKCS12_bio(fp, NULL); - BIO_free(fp); if(!p12) { - failf(data, "error reading PKCS12 file '%s'", cert_file); + failf(data, "error reading PKCS12 file '%s'", + cert_bio ? "(memory blob)" : cert_file); return 0; } @@ -839,8 +920,10 @@ int cert_stuff(struct connectdata *conn, return 0; } - if(!key_file) + if((!key_file) && (!key_bio)) { key_file = cert_file; + key_bio = cert_bio; + } else file_type = do_file_type(key_type); @@ -850,9 +933,12 @@ int cert_stuff(struct connectdata *conn, break; /* FALLTHROUGH */ case SSL_FILETYPE_ASN1: - if(SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type) != 1) { + cert_use_result = key_bio ? + SSL_CTX_use_PrivateKey_bio(ctx, key_bio, file_type, key_passwd) : + SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type); + if(cert_use_result != 1) { failf(data, "unable to set private key file: '%s' type %s", - key_file, key_type?key_type:"PEM"); + key_file?key_file:"(memory blob)", key_type?key_type:"PEM"); return 0; } break; @@ -1012,24 +1098,14 @@ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) */ static int Curl_ossl_init(void) { -#ifdef ENABLE_SSLKEYLOGFILE - char *keylog_file_name; -#endif - OPENSSL_load_builtin_modules(); #ifdef USE_OPENSSL_ENGINE ENGINE_load_builtin_engines(); #endif - /* OPENSSL_config(NULL); is "strongly recommended" to use but unfortunately - that function makes an exit() call on wrongly formatted config files - which makes it hard to use in some situations. OPENSSL_config() itself - calls CONF_modules_load_file() and we use that instead and we ignore - its return code! */ - - /* CONF_MFLAGS_DEFAULT_SECTION introduced some time between 0.9.8b and - 0.9.8e */ +/* CONF_MFLAGS_DEFAULT_SECTION was introduced some time between 0.9.8b and + 0.9.8e */ #ifndef CONF_MFLAGS_DEFAULT_SECTION #define CONF_MFLAGS_DEFAULT_SECTION 0x0 #endif @@ -1054,26 +1130,7 @@ static int Curl_ossl_init(void) OpenSSL_add_all_algorithms(); #endif -#ifdef ENABLE_SSLKEYLOGFILE - if(!keylog_file_fp) { - keylog_file_name = curl_getenv("SSLKEYLOGFILE"); - if(keylog_file_name) { - keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); - if(keylog_file_fp) { -#ifdef WIN32 - if(setvbuf(keylog_file_fp, NULL, _IONBF, 0)) -#else - if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) -#endif - { - fclose(keylog_file_fp); - keylog_file_fp = NULL; - } - } - Curl_safefree(keylog_file_name); - } - } -#endif + Curl_tls_keylog_open(); /* Initialize the extra data indexes */ if(ossl_get_ssl_conn_index() < 0 || ossl_get_ssl_sockindex_index() < 0) @@ -1116,12 +1173,7 @@ static void Curl_ossl_cleanup(void) #endif #endif -#ifdef ENABLE_SSLKEYLOGFILE - if(keylog_file_fp) { - fclose(keylog_file_fp); - keylog_file_fp = NULL; - } -#endif + Curl_tls_keylog_close(); } /* @@ -1266,19 +1318,19 @@ static struct curl_slist *Curl_ossl_engines_list(struct Curl_easy *data) return list; } - static void ossl_close(struct ssl_connect_data *connssl) { - if(BACKEND->handle) { - (void)SSL_shutdown(BACKEND->handle); - SSL_set_connect_state(BACKEND->handle); + struct ssl_backend_data *backend = connssl->backend; + if(backend->handle) { + (void)SSL_shutdown(backend->handle); + SSL_set_connect_state(backend->handle); - SSL_free(BACKEND->handle); - BACKEND->handle = NULL; + SSL_free(backend->handle); + backend->handle = NULL; } - if(BACKEND->ctx) { - SSL_CTX_free(BACKEND->ctx); - BACKEND->ctx = NULL; + if(backend->ctx) { + SSL_CTX_free(backend->ctx); + backend->ctx = NULL; } } @@ -1288,7 +1340,9 @@ static void ossl_close(struct ssl_connect_data *connssl) static void Curl_ossl_close(struct connectdata *conn, int sockindex) { ossl_close(&conn->ssl[sockindex]); +#ifndef CURL_DISABLE_PROXY ossl_close(&conn->proxy_ssl[sockindex]); +#endif } /* @@ -1307,6 +1361,7 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) int buffsize; int err; bool done = FALSE; + struct ssl_backend_data *backend = connssl->backend; #ifndef CURL_DISABLE_FTP /* This has only been tested on the proftpd server, and the mod_tls code @@ -1315,10 +1370,10 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) we do not send one. Let's hope other servers do the same... */ if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - (void)SSL_shutdown(BACKEND->handle); + (void)SSL_shutdown(backend->handle); #endif - if(BACKEND->handle) { + if(backend->handle) { buffsize = (int)sizeof(buf); while(!done) { int what = SOCKET_READABLE(conn->sock[sockindex], @@ -1328,8 +1383,8 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) /* Something to read, let's do it and hope that it is the close notify alert from the server */ - nread = (ssize_t)SSL_read(BACKEND->handle, buf, buffsize); - err = SSL_get_error(BACKEND->handle, (int)nread); + nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); + err = SSL_get_error(backend->handle, (int)nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -1374,7 +1429,7 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) if(data->set.verbose) { #ifdef HAVE_SSL_GET_SHUTDOWN - switch(SSL_get_shutdown(BACKEND->handle)) { + switch(SSL_get_shutdown(backend->handle)) { case SSL_SENT_SHUTDOWN: infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n"); break; @@ -1389,8 +1444,8 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) #endif } - SSL_free(BACKEND->handle); - BACKEND->handle = NULL; + SSL_free(backend->handle); + backend->handle = NULL; } return retval; } @@ -1514,10 +1569,16 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert) CURLcode result = CURLE_OK; bool dNSName = FALSE; /* if a dNSName field exists in the cert */ bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */ - const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : - conn->host.name; +#ifndef CURL_DISABLE_PROXY + const char * const hostname = SSL_IS_PROXY() ? + conn->http_proxy.host.name : conn->host.name; const char * const dispname = SSL_IS_PROXY() ? conn->http_proxy.host.dispname : conn->host.dispname; +#else + /* disabled proxy support */ + const char * const hostname = conn->host.name; + const char * const dispname = conn->host.dispname; +#endif #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && @@ -1536,8 +1597,13 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert) altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); if(altnames) { +#ifdef OPENSSL_IS_BORINGSSL + size_t numalts; + size_t i; +#else int numalts; int i; +#endif bool dnsmatched = FALSE; bool ipmatched = FALSE; @@ -1567,11 +1633,10 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert) 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 and IA5String the data will be ASCII" + type itself: for example for an IA5String the data will be ASCII" - Gisle researched the OpenSSL sources: - "I checked the 0.9.6 and 0.9.8 sources before my patch and - it always 0-terminates an IA5String." + It has been however verified that in 0.9.6 and 0.9.7, IA5String + is always null-terminated. */ if((altlen == strlen(altptr)) && /* if this isn't true, there was an embedded zero in the name @@ -1635,8 +1700,7 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert) /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input is already UTF-8 encoded. We check for this case and copy the raw string manually to avoid the problem. This code can be made - conditional in the future when OpenSSL has been fixed. Work-around - brought by Alexis S. L. Carvalho. */ + conditional in the future when OpenSSL has been fixed. */ if(tmp) { if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { j = ASN1_STRING_length(tmp); @@ -1706,13 +1770,13 @@ static CURLcode verifystatus(struct connectdata *conn, const unsigned char *p; CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; - OCSP_RESPONSE *rsp = NULL; OCSP_BASICRESP *br = NULL; X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; + struct ssl_backend_data *backend = connssl->backend; - long len = SSL_get_tlsext_status_ocsp_resp(BACKEND->handle, &status); + long len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); if(!status) { failf(data, "No OCSP response received"); @@ -1742,8 +1806,8 @@ static CURLcode verifystatus(struct connectdata *conn, goto end; } - ch = SSL_get_peer_cert_chain(BACKEND->handle); - st = SSL_CTX_get_cert_store(BACKEND->ctx); + ch = SSL_get_peer_cert_chain(backend->handle); + st = SSL_CTX_get_cert_store(backend->ctx); #if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \ (defined(LIBRESSL_VERSION_NUMBER) && \ @@ -1819,7 +1883,8 @@ static CURLcode verifystatus(struct connectdata *conn, } end: - if(br) OCSP_BASICRESP_free(br); + if(br) + OCSP_BASICRESP_free(br); OCSP_RESPONSE_free(rsp); return result; @@ -2156,9 +2221,100 @@ get_ssl_version_txt(SSL *ssl) } #endif +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ static CURLcode -set_ssl_version_min_max(long *ctx_options, struct connectdata *conn, - int sockindex) +set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) +{ + /* first, TLS min version... */ + long curl_ssl_version_min = SSL_CONN_CONFIG(version); + long curl_ssl_version_max; + + /* convert cURL min SSL version option to OpenSSL constant */ +#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER) + uint16_t ossl_ssl_version_min = 0; + uint16_t ossl_ssl_version_max = 0; +#else + long ossl_ssl_version_min = 0; + long ossl_ssl_version_max = 0; +#endif + switch(curl_ssl_version_min) { + case CURL_SSLVERSION_TLSv1: /* TLS 1.x */ + case CURL_SSLVERSION_TLSv1_0: + ossl_ssl_version_min = TLS1_VERSION; + break; + case CURL_SSLVERSION_TLSv1_1: + ossl_ssl_version_min = TLS1_1_VERSION; + break; + case CURL_SSLVERSION_TLSv1_2: + ossl_ssl_version_min = TLS1_2_VERSION; + break; +#ifdef TLS1_3_VERSION + case CURL_SSLVERSION_TLSv1_3: + ossl_ssl_version_min = TLS1_3_VERSION; + break; +#endif + } + + /* CURL_SSLVERSION_DEFAULT means that no option was selected. + We don't want to pass 0 to SSL_CTX_set_min_proto_version as + it would enable all versions down to the lowest supported by + the library. + So we skip this, and stay with the OS default + */ + if(curl_ssl_version_min != CURL_SSLVERSION_DEFAULT) { + if(!SSL_CTX_set_min_proto_version(ctx, ossl_ssl_version_min)) { + return CURLE_SSL_CONNECT_ERROR; + } + } + + /* ... then, TLS max version */ + curl_ssl_version_max = SSL_CONN_CONFIG(version_max); + + /* convert cURL max SSL version option to OpenSSL constant */ + switch(curl_ssl_version_max) { + case CURL_SSLVERSION_MAX_TLSv1_0: + ossl_ssl_version_max = TLS1_VERSION; + break; + case CURL_SSLVERSION_MAX_TLSv1_1: + ossl_ssl_version_max = TLS1_1_VERSION; + break; + case CURL_SSLVERSION_MAX_TLSv1_2: + ossl_ssl_version_max = TLS1_2_VERSION; + break; +#ifdef TLS1_3_VERSION + case CURL_SSLVERSION_MAX_TLSv1_3: + ossl_ssl_version_max = TLS1_3_VERSION; + break; +#endif + case CURL_SSLVERSION_MAX_NONE: /* none selected */ + case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */ + default: + /* SSL_CTX_set_max_proto_version states that: + setting the maximum to 0 will enable + protocol versions up to the highest version + supported by the library */ + ossl_ssl_version_max = 0; + break; + } + + if(!SSL_CTX_set_max_proto_version(ctx, ossl_ssl_version_max)) { + return CURLE_SSL_CONNECT_ERROR; + } + + return CURLE_OK; +} +#endif + +#ifdef OPENSSL_IS_BORINGSSL +typedef uint32_t ctx_option_t; +#else +typedef long ctx_option_t; +#endif + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */ +static CURLcode +set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, + struct connectdata *conn, int sockindex) { #if (OPENSSL_VERSION_NUMBER < 0x1000100FL) || !defined(TLS1_3_VERSION) /* convoluted #if condition just to avoid compiler warnings on unused @@ -2173,7 +2329,7 @@ set_ssl_version_min_max(long *ctx_options, struct connectdata *conn, #ifdef TLS1_3_VERSION { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SSL_CTX_set_max_proto_version(BACKEND->ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); *ctx_options |= SSL_OP_NO_TLSv1_2; } #else @@ -2230,6 +2386,7 @@ set_ssl_version_min_max(long *ctx_options, struct connectdata *conn, } return CURLE_OK; } +#endif /* The "new session" callback must return zero if the session can be removed * or non-zero if the session has been put into the session cache. @@ -2296,30 +2453,43 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) X509_LOOKUP *lookup = NULL; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - long ctx_options = 0; + ctx_option_t ctx_options = 0; + #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME bool sni; +#ifndef CURL_DISABLE_PROXY const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + const char * const hostname = conn->host.name; +#endif + #ifdef ENABLE_IPV6 struct in6_addr addr; #else struct in_addr addr; #endif #endif +#ifndef CURL_DISABLE_PROXY long * const certverifyresult = SSL_IS_PROXY() ? &data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult; +#else + long * const certverifyresult = &data->set.ssl.certverifyresult; +#endif const long int ssl_version = SSL_CONN_CONFIG(version); #ifdef USE_TLS_SRP const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(authtype); #endif char * const ssl_cert = SSL_SET_OPTION(cert); + const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(cert_blob); const char * const ssl_cert_type = SSL_SET_OPTION(cert_type); const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile); const char * const ssl_capath = SSL_CONN_CONFIG(CApath); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile); char error_buffer[256]; + struct ssl_backend_data *backend = connssl->backend; + bool imported_native_ca = false; DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); @@ -2378,25 +2548,25 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) return CURLE_SSL_CONNECT_ERROR; } - if(BACKEND->ctx) - SSL_CTX_free(BACKEND->ctx); - BACKEND->ctx = SSL_CTX_new(req_method); + if(backend->ctx) + SSL_CTX_free(backend->ctx); + backend->ctx = SSL_CTX_new(req_method); - if(!BACKEND->ctx) { + if(!backend->ctx) { failf(data, "SSL: couldn't create a context: %s", ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer))); return CURLE_OUT_OF_MEMORY; } #ifdef SSL_MODE_RELEASE_BUFFERS - SSL_CTX_set_mode(BACKEND->ctx, SSL_MODE_RELEASE_BUFFERS); + SSL_CTX_set_mode(backend->ctx, SSL_MODE_RELEASE_BUFFERS); #endif #ifdef SSL_CTRL_SET_MSG_CALLBACK if(data->set.fdebug && data->set.verbose) { /* the SSL trace callback is only used for verbose logging */ - SSL_CTX_set_msg_callback(BACKEND->ctx, ssl_tls_trace); - SSL_CTX_set_msg_callback_arg(BACKEND->ctx, conn); + SSL_CTX_set_msg_callback(backend->ctx, ssl_tls_trace); + SSL_CTX_set_msg_callback_arg(backend->ctx, conn); } #endif @@ -2459,55 +2629,73 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) #endif switch(ssl_version) { - case CURL_SSLVERSION_SSLv3: - ctx_options |= SSL_OP_NO_SSLv2; - ctx_options |= SSL_OP_NO_TLSv1; -#if OPENSSL_VERSION_NUMBER >= 0x1000100FL - ctx_options |= SSL_OP_NO_TLSv1_1; - ctx_options |= SSL_OP_NO_TLSv1_2; -#ifdef TLS1_3_VERSION - ctx_options |= SSL_OP_NO_TLSv1_3; -#endif + /* "--sslv2" option means SSLv2 only, disable all others */ + case CURL_SSLVERSION_SSLv2: +#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ + SSL_CTX_set_min_proto_version(backend->ctx, SSL2_VERSION); + SSL_CTX_set_max_proto_version(backend->ctx, SSL2_VERSION); +#else + ctx_options |= SSL_OP_NO_SSLv3; + ctx_options |= SSL_OP_NO_TLSv1; +# if OPENSSL_VERSION_NUMBER >= 0x1000100FL + ctx_options |= SSL_OP_NO_TLSv1_1; + ctx_options |= SSL_OP_NO_TLSv1_2; +# ifdef TLS1_3_VERSION + ctx_options |= SSL_OP_NO_TLSv1_3; +# endif +# endif #endif - break; - - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - /* asking for any TLS version as the minimum, means no SSL versions - allowed */ - ctx_options |= SSL_OP_NO_SSLv2; - ctx_options |= SSL_OP_NO_SSLv3; - result = set_ssl_version_min_max(&ctx_options, conn, sockindex); - if(result != CURLE_OK) - return result; - break; + break; - case CURL_SSLVERSION_SSLv2: - ctx_options |= SSL_OP_NO_SSLv3; - ctx_options |= SSL_OP_NO_TLSv1; -#if OPENSSL_VERSION_NUMBER >= 0x1000100FL - ctx_options |= SSL_OP_NO_TLSv1_1; - ctx_options |= SSL_OP_NO_TLSv1_2; -#ifdef TLS1_3_VERSION - ctx_options |= SSL_OP_NO_TLSv1_3; + /* "--sslv3" option means SSLv3 only, disable all others */ + case CURL_SSLVERSION_SSLv3: +#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ + SSL_CTX_set_min_proto_version(backend->ctx, SSL3_VERSION); + SSL_CTX_set_max_proto_version(backend->ctx, SSL3_VERSION); +#else + ctx_options |= SSL_OP_NO_SSLv2; + ctx_options |= SSL_OP_NO_TLSv1; +# if OPENSSL_VERSION_NUMBER >= 0x1000100FL + ctx_options |= SSL_OP_NO_TLSv1_1; + ctx_options |= SSL_OP_NO_TLSv1_2; +# ifdef TLS1_3_VERSION + ctx_options |= SSL_OP_NO_TLSv1_3; +# endif +# endif #endif + break; + + /* "--tlsv" options mean TLS >= version */ + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */ + case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */ + case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */ + case CURL_SSLVERSION_TLSv1_2: /* TLS >= version 1.2 */ + case CURL_SSLVERSION_TLSv1_3: /* TLS >= version 1.3 */ + /* asking for any TLS version as the minimum, means no SSL versions + allowed */ + ctx_options |= SSL_OP_NO_SSLv2; + ctx_options |= SSL_OP_NO_SSLv3; + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ + result = set_ssl_version_min_max(backend->ctx, conn); +#else + result = set_ssl_version_min_max_legacy(&ctx_options, conn, sockindex); #endif - break; + if(result != CURLE_OK) + return result; + break; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; } - SSL_CTX_set_options(BACKEND->ctx, ctx_options); + SSL_CTX_set_options(backend->ctx, ctx_options); #ifdef HAS_NPN if(conn->bits.tls_enable_npn) - SSL_CTX_set_next_proto_select_cb(BACKEND->ctx, select_next_proto_cb, conn); + SSL_CTX_set_next_proto_select_cb(backend->ctx, select_next_proto_cb, conn); #endif #ifdef HAS_ALPN @@ -2516,8 +2704,11 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) unsigned char protocols[128]; #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2 && - (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)) { + if(data->set.httpversion >= CURL_HTTP_VERSION_2 +#ifndef CURL_DISABLE_PROXY + && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) +#endif + ) { protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN; memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID, @@ -2535,14 +2726,37 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) /* expects length prefixed preference ordered list of protocols in wire * format */ - SSL_CTX_set_alpn_protos(BACKEND->ctx, protocols, cur); + SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur); } #endif - if(ssl_cert || ssl_cert_type) { - if(!cert_stuff(conn, BACKEND->ctx, ssl_cert, ssl_cert_type, - SSL_SET_OPTION(key), SSL_SET_OPTION(key_type), - SSL_SET_OPTION(key_passwd))) { + if(ssl_cert || ssl_cert_blob || ssl_cert_type) { + BIO *ssl_cert_bio = NULL; + BIO *ssl_key_bio = NULL; + int result_cert_stuff; + if(ssl_cert_blob) { + /* the typecast of blob->len is fine since it is guaranteed to never be + larger than CURL_MAX_INPUT_LENGTH */ + ssl_cert_bio = BIO_new_mem_buf(ssl_cert_blob->data, + (int)ssl_cert_blob->len); + if(!ssl_cert_bio) + return CURLE_SSL_CERTPROBLEM; + } + if(SSL_SET_OPTION(key_blob)) { + ssl_key_bio = BIO_new_mem_buf(SSL_SET_OPTION(key_blob)->data, + (int)SSL_SET_OPTION(key_blob)->len); + if(!ssl_key_bio) + return CURLE_SSL_CERTPROBLEM; + } + result_cert_stuff = cert_stuff(conn, backend->ctx, + ssl_cert, ssl_cert_bio, ssl_cert_type, + SSL_SET_OPTION(key), ssl_key_bio, + SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd)); + if(ssl_cert_bio) + BIO_free(ssl_cert_bio); + if(ssl_key_bio) + BIO_free(ssl_key_bio); + if(!result_cert_stuff) { /* failf() is already done in cert_stuff() */ return CURLE_SSL_CERTPROBLEM; } @@ -2552,7 +2766,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) if(!ciphers) ciphers = (char *)DEFAULT_CIPHER_SELECTION; if(ciphers) { - if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) { + if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } @@ -2563,7 +2777,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) { char *ciphers13 = SSL_CONN_CONFIG(cipher_list13); if(ciphers13) { - if(!SSL_CTX_set_ciphersuites(BACKEND->ctx, ciphers13)) { + if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) { failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13); return CURLE_SSL_CIPHER; } @@ -2574,7 +2788,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) #ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH /* OpenSSL 1.1.1 requires clients to opt-in for PHA */ - SSL_CTX_set_post_handshake_auth(BACKEND->ctx, 1); + SSL_CTX_set_post_handshake_auth(backend->ctx, 1); #endif #ifdef USE_TLS_SRP @@ -2583,18 +2797,18 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) infof(data, "Using TLS-SRP username: %s\n", ssl_username); - if(!SSL_CTX_set_srp_username(BACKEND->ctx, ssl_username)) { + if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) { failf(data, "Unable to set SRP user name"); return CURLE_BAD_FUNCTION_ARGUMENT; } - if(!SSL_CTX_set_srp_password(BACKEND->ctx, SSL_SET_OPTION(password))) { + if(!SSL_CTX_set_srp_password(backend->ctx, SSL_SET_OPTION(password))) { failf(data, "failed setting SRP password"); return CURLE_BAD_FUNCTION_ARGUMENT; } if(!SSL_CONN_CONFIG(cipher_list)) { infof(data, "Setting cipher list SRP\n"); - if(!SSL_CTX_set_cipher_list(BACKEND->ctx, "SRP")) { + if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) { failf(data, "failed setting SRP cipher list"); return CURLE_SSL_CIPHER; } @@ -2602,11 +2816,186 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) } #endif + +#if defined(USE_WIN32_CRYPTO) + /* Import certificates from the Windows root certificate store if requested. + https://stackoverflow.com/questions/9507184/ + https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 + https://tools.ietf.org/html/rfc5280 */ + if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) && + (SSL_SET_OPTION(native_ca_store))) { + X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); + HCERTSTORE hStore = CertOpenSystemStoreA((HCRYPTPROV_LEGACY)NULL, "ROOT"); + + if(hStore) { + PCCERT_CONTEXT pContext = NULL; + /* The array of enhanced key usage OIDs will vary 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; + + /* This loop makes a best effort to import all valid certificates from + the MS root store. If a certificate cannot be imported it is skipped. + 'result' is used to store only hard-fail conditions (such as out of + memory) that cause an early break. */ + result = CURLE_OK; + for(;;) { + X509 *x509; + FILETIME now; + BYTE key_usage[2]; + DWORD req_size; + const unsigned char *encoded_cert; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + char cert_name[256]; +#endif + + pContext = CertEnumCertificatesInStore(hStore, pContext); + if(!pContext) + break; + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, + NULL, cert_name, sizeof(cert_name))) { + strcpy(cert_name, "Unknown"); + } + infof(data, "SSL: Checking cert \"%s\"\n", cert_name); +#endif + + encoded_cert = (const unsigned char *)pContext->pbCertEncoded; + if(!encoded_cert) + continue; + + GetSystemTimeAsFileTime(&now); + if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || + CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) + continue; + + /* If key usage exists check for signing attribute */ + if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, + pContext->pCertInfo, + key_usage, sizeof(key_usage))) { + if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) + continue; + } + else if(GetLastError()) + continue; + + /* If enhanced key usage exists check for server auth attribute. + * + * Note "In a Microsoft environment, a certificate might also have EKU + * extended properties that specify valid uses for the certificate." + * The call below checks both, and behavior varies depending on what is + * found. For more details see CertGetEnhancedKeyUsage doc. + */ + if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { + if(req_size && req_size > enhkey_usage_size) { + void *tmp = realloc(enhkey_usage, req_size); + + if(!tmp) { + failf(data, "SSL: Out of memory allocating for OID list"); + result = CURLE_OUT_OF_MEMORY; + break; + } + + enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; + enhkey_usage_size = req_size; + } + + if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { + if(!enhkey_usage->cUsageIdentifier) { + /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is + good for all uses. If it returns zero, the certificate has no + valid uses." */ + if(GetLastError() != CRYPT_E_NOT_FOUND) + continue; + } + else { + DWORD i; + bool found = false; + + for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { + if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, + enhkey_usage->rgpszUsageIdentifier[i])) { + found = true; + break; + } + } + + if(!found) + continue; + } + } + else + continue; + } + else + continue; + + x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if(!x509) + continue; + + /* Try to import the certificate. This may fail for legitimate reasons + such as duplicate certificate, which is allowed by MS but not + OpenSSL. */ + if(X509_STORE_add_cert(store, x509) == 1) { +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + infof(data, "SSL: Imported cert \"%s\"\n", cert_name); +#endif + imported_native_ca = true; + } + X509_free(x509); + } + + free(enhkey_usage); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + if(result) + return result; + } + if(imported_native_ca) + infof(data, "successfully imported windows ca store\n"); + else + infof(data, "error importing windows ca store, continuing anyway\n"); + } +#endif + +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ + { + if(ssl_cafile) { + if(!SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) { + if(verifypeer) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; + } + /* Continue with a warning if no certificate verif is required. */ + infof(data, "error setting certificate file, continuing anyway\n"); + } + infof(data, " CAfile: %s\n", ssl_cafile); + } + if(ssl_capath) { + if(!SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) { + if(verifypeer) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; + } + /* Continue with a warning if no certificate verif is required. */ + infof(data, "error setting certificate path, continuing anyway\n"); + } + infof(data, " CApath: %s\n", ssl_capath); + } + } +#else if(ssl_cafile || ssl_capath) { /* tell SSL where to find CA certificates that are used to verify the servers certificate. */ - if(!SSL_CTX_load_verify_locations(BACKEND->ctx, ssl_cafile, ssl_capath)) { - if(verifypeer) { + if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) { + if(verifypeer && !imported_native_ca) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", @@ -2614,7 +3003,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) ssl_capath ? ssl_capath : "none"); return CURLE_SSL_CACERT_BADFILE; } - /* Just continue with a warning if no strict certificate verification + /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); @@ -2629,18 +3018,20 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) ssl_cafile ? ssl_cafile : "none", ssl_capath ? ssl_capath : "none"); } +#endif + #ifdef CURL_CA_FALLBACK - else if(verifypeer) { + if(verifypeer && !ssl_cafile && !ssl_capath && !imported_native_ca) { /* verifying the peer without any CA certificates won't work so use openssl's built in default as fallback */ - SSL_CTX_set_default_verify_paths(BACKEND->ctx); + SSL_CTX_set_default_verify_paths(backend->ctx); } #endif if(ssl_crlfile) { /* tell SSL where to find CRL file that is used to check certificate * revocation */ - lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(BACKEND->ctx), + lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx), X509_LOOKUP_file()); if(!lookup || (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { @@ -2649,37 +3040,50 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) } /* Everything is fine. */ infof(data, "successfully load CRL file:\n"); - X509_STORE_set_flags(SSL_CTX_get_cert_store(BACKEND->ctx), + X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); infof(data, " CRLfile: %s\n", ssl_crlfile); } - /* Try building a chain using issuers in the trusted store first to avoid - problems with server-sent legacy intermediates. - Newer versions of OpenSSL do alternate chain checking by default which - gives us the same fix without as much of a performance hit (slight), so we - prefer that if available. - https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest - */ -#if defined(X509_V_FLAG_TRUSTED_FIRST) && !defined(X509_V_FLAG_NO_ALT_CHAINS) if(verifypeer) { - X509_STORE_set_flags(SSL_CTX_get_cert_store(BACKEND->ctx), + /* Try building a chain using issuers in the trusted store first to avoid + problems with server-sent legacy intermediates. Newer versions of + OpenSSL do alternate chain checking by default but we do not know how to + determine that in a reliable manner. + https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest + */ +#if defined(X509_V_FLAG_TRUSTED_FIRST) + X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), X509_V_FLAG_TRUSTED_FIRST); - } #endif +#ifdef X509_V_FLAG_PARTIAL_CHAIN + if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) { + /* Have intermediate certificates in the trust store be treated as + trust-anchors, in the same way as self-signed root CA certificates + are. This allows users to verify servers using the intermediate cert + only, instead of needing the whole chain. + + Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we + cannot do partial chains with CRL check. + */ + X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), + X509_V_FLAG_PARTIAL_CHAIN); + } +#endif + } /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(BACKEND->ctx, + SSL_CTX_set_verify(backend->ctx, verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */ -#if defined(ENABLE_SSLKEYLOGFILE) && defined(HAVE_KEYLOG_CALLBACK) - if(keylog_file_fp) { - SSL_CTX_set_keylog_callback(BACKEND->ctx, ossl_keylog_callback); +#ifdef HAVE_KEYLOG_CALLBACK + if(Curl_tls_keylog_enabled()) { + SSL_CTX_set_keylog_callback(backend->ctx, ossl_keylog_callback); } #endif @@ -2687,14 +3091,16 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) * callback. Use the "external storage" mode to avoid that OpenSSL creates * an internal session cache. */ - SSL_CTX_set_session_cache_mode(BACKEND->ctx, + SSL_CTX_set_session_cache_mode(backend->ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_new_cb(BACKEND->ctx, ossl_new_session_cb); + SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb); /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { - result = (*data->set.ssl.fsslctx)(data, BACKEND->ctx, + Curl_set_in_callback(data, true); + result = (*data->set.ssl.fsslctx)(data, backend->ctx, data->set.ssl.fsslctxp); + Curl_set_in_callback(data, false); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; @@ -2702,10 +3108,10 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) } /* Lets make an SSL structure */ - if(BACKEND->handle) - SSL_free(BACKEND->handle); - BACKEND->handle = SSL_new(BACKEND->ctx); - if(!BACKEND->handle) { + if(backend->handle) + SSL_free(backend->handle); + backend->handle = SSL_new(backend->ctx); + if(!backend->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } @@ -2713,23 +3119,23 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) if(SSL_CONN_CONFIG(verifystatus)) - SSL_set_tlsext_status_type(BACKEND->handle, TLSEXT_STATUSTYPE_ocsp); + SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp); #endif #if defined(OPENSSL_IS_BORINGSSL) && defined(ALLOW_RENEG) - SSL_set_renegotiate_mode(BACKEND->handle, ssl_renegotiate_freely); + SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely); #endif - SSL_set_connect_state(BACKEND->handle); + SSL_set_connect_state(backend->handle); - BACKEND->server_cert = 0x0; + backend->server_cert = 0x0; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && #endif sni && - !SSL_set_tlsext_host_name(BACKEND->handle, hostname)) + !SSL_set_tlsext_host_name(backend->handle, hostname)) infof(data, "WARNING: failed to configure server name indication (SNI) " "TLS extension\n"); #endif @@ -2743,14 +3149,14 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) if(connectdata_idx >= 0 && sockindex_idx >= 0) { /* Store the data needed for the "new session" callback. * The sockindex is stored as a pointer to an array element. */ - SSL_set_ex_data(BACKEND->handle, connectdata_idx, conn); - SSL_set_ex_data(BACKEND->handle, sockindex_idx, conn->sock + sockindex); + SSL_set_ex_data(backend->handle, connectdata_idx, conn); + SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex); } Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) { /* we got a session id, use it! */ - if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) { + if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_sessionid_unlock(conn); failf(data, "SSL: SSL_set_session failed: %s", ossl_strerror(ERR_get_error(), error_buffer, @@ -2763,6 +3169,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) Curl_ssl_sessionid_unlock(conn); } +#ifndef CURL_DISABLE_PROXY if(conn->proxy_ssl[sockindex].use) { BIO *const bio = BIO_new(BIO_f_ssl()); SSL *handle = conn->proxy_ssl[sockindex].backend->handle; @@ -2770,9 +3177,11 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) DEBUGASSERT(handle != NULL); DEBUGASSERT(bio != NULL); BIO_set_ssl(bio, handle, FALSE); - SSL_set_bio(BACKEND->handle, bio, bio); + SSL_set_bio(backend->handle, bio, bio); } - else if(!SSL_set_fd(BACKEND->handle, (int)sockfd)) { + else +#endif + if(!SSL_set_fd(backend->handle, (int)sockfd)) { /* pass the raw socket into the SSL layers */ failf(data, "SSL: SSL_set_fd failed: %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); @@ -2789,26 +3198,34 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) struct Curl_easy *data = conn->data; int err; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; +#ifndef CURL_DISABLE_PROXY long * const certverifyresult = SSL_IS_PROXY() ? &data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult; +#else + long * const certverifyresult = &data->set.ssl.certverifyresult; +#endif + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); ERR_clear_error(); - err = SSL_connect(BACKEND->handle); - /* If keylogging is enabled but the keylog callback is not supported then log - secrets here, immediately after SSL_connect by using tap_ssl_key. */ -#if defined(ENABLE_SSLKEYLOGFILE) && !defined(HAVE_KEYLOG_CALLBACK) - tap_ssl_key(BACKEND->handle, &BACKEND->tap_state); + err = SSL_connect(backend->handle); +#ifndef HAVE_KEYLOG_CALLBACK + if(Curl_tls_keylog_enabled()) { + /* If key logging is enabled, wait for the handshake to complete and then + * proceed with logging secrets (for TLS 1.2 or older). + */ + ossl_log_tls12_secret(backend->handle, &backend->keylog_done); + } #endif /* 1 is fine 0 is "not successful but was shut down controlled" <0 is "handshake was not successful, because a fatal error occurred" */ if(1 != err) { - int detail = SSL_get_error(BACKEND->handle, err); + int detail = SSL_get_error(backend->handle, err); if(SSL_ERROR_WANT_READ == detail) { connssl->connecting_state = ssl_connect_2_reading; @@ -2848,7 +3265,7 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) (reason == SSL_R_CERTIFICATE_VERIFY_FAILED)) { result = CURLE_PEER_FAILED_VERIFICATION; - lerr = SSL_get_verify_result(BACKEND->handle); + lerr = SSL_get_verify_result(backend->handle); if(lerr != X509_V_OK) { *certverifyresult = lerr; msnprintf(error_buffer, sizeof(error_buffer), @@ -2872,11 +3289,21 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) * the SO_ERROR is also lost. */ if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { +#ifndef CURL_DISABLE_PROXY const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; +#else + const char * const hostname = conn->host.name; + const long int port = conn->remote_port; +#endif + char extramsg[80]=""; + int sockerr = SOCKERRNO; + if(sockerr && detail == SSL_ERROR_SYSCALL) + Curl_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%ld ", - SSL_ERROR_to_str(detail), hostname, port); + extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), + hostname, port); return result; } @@ -2892,8 +3319,8 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) /* Informational message */ infof(data, "SSL connection using %s / %s\n", - get_ssl_version_txt(BACKEND->handle), - SSL_get_cipher(BACKEND->handle)); + get_ssl_version_txt(backend->handle), + SSL_get_cipher(backend->handle)); #ifdef HAS_ALPN /* Sets data and len to negotiated protocol, len is 0 if no protocol was @@ -2902,7 +3329,7 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) if(conn->bits.tls_enable_alpn) { const unsigned char *neg_protocol; unsigned int len; - SSL_get0_alpn_selected(BACKEND->handle, &neg_protocol, &len); + SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); if(len != 0) { infof(data, "ALPN, server accepted to use %.*s\n", len, neg_protocol); @@ -2952,7 +3379,7 @@ do { \ Curl_ssl_push_certinfo_len(data, _num, _label, ptr, info_len); \ if(1 != BIO_reset(mem)) \ break; \ -} WHILE_FALSE +} while(0) static void pubkey_show(struct Curl_easy *data, BIO *mem, @@ -2984,31 +3411,28 @@ do { \ if(_type->_name) { \ pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \ } \ -} WHILE_FALSE +} while(0) #endif -static int X509V3_ext(struct Curl_easy *data, +static void X509V3_ext(struct Curl_easy *data, int certnum, CONST_EXTS STACK_OF(X509_EXTENSION) *exts) { int i; - size_t j; if((int)sk_X509_EXTENSION_num(exts) <= 0) /* no extensions, bail out */ - return 1; + return; for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) { ASN1_OBJECT *obj; X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i); BUF_MEM *biomem; - char buf[512]; - char *ptr = buf; char namebuf[128]; BIO *bio_out = BIO_new(BIO_s_mem()); if(!bio_out) - return 1; + return; obj = X509_EXTENSION_get_object(ext); @@ -3018,28 +3442,18 @@ static int X509V3_ext(struct Curl_easy *data, ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); BIO_get_mem_ptr(bio_out, &biomem); - - for(j = 0; j < (size_t)biomem->length; j++) { - const char *sep = ""; - if(biomem->data[j] == '\n') { - sep = ", "; - j++; /* skip the newline */ - }; - while((j<(size_t)biomem->length) && (biomem->data[j] == ' ')) - j++; - if(j<(size_t)biomem->length) - ptr += msnprintf(ptr, sizeof(buf)-(ptr-buf), "%s%c", sep, - biomem->data[j]); - } - - Curl_ssl_push_certinfo(data, certnum, namebuf, buf); - + Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data, + biomem->length); BIO_free(bio_out); - } - return 0; /* all is fine */ } +#ifdef OPENSSL_IS_BORINGSSL +typedef size_t numcert_t; +#else +typedef int numcert_t; +#endif + static CURLcode get_cert_chain(struct connectdata *conn, struct ssl_connect_data *connssl) @@ -3048,24 +3462,25 @@ static CURLcode get_cert_chain(struct connectdata *conn, STACK_OF(X509) *sk; int i; struct Curl_easy *data = conn->data; - int numcerts; + numcert_t numcerts; BIO *mem; + struct ssl_backend_data *backend = connssl->backend; - sk = SSL_get_peer_cert_chain(BACKEND->handle); + sk = SSL_get_peer_cert_chain(backend->handle); if(!sk) { return CURLE_OUT_OF_MEMORY; } numcerts = sk_X509_num(sk); - result = Curl_ssl_init_certinfo(data, numcerts); + result = Curl_ssl_init_certinfo(data, (int)numcerts); if(result) { return result; } mem = BIO_new(BIO_s_mem()); - for(i = 0; i < numcerts; i++) { + for(i = 0; i < (int)numcerts; i++) { ASN1_INTEGER *num; X509 *x = sk_X509_value(sk, i); EVP_PKEY *pubkey = NULL; @@ -3091,18 +3506,25 @@ static CURLcode get_cert_chain(struct connectdata *conn, #if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS) { - const X509_ALGOR *palg = NULL; - ASN1_STRING *a = ASN1_STRING_new(); - if(a) { - X509_get0_signature(&psig, &palg, x); - X509_signature_print(mem, ARG2_X509_signature_print palg, a); - ASN1_STRING_free(a); - - if(palg) { - i2a_ASN1_OBJECT(mem, palg->algorithm); + const X509_ALGOR *sigalg = NULL; + X509_PUBKEY *xpubkey = NULL; + ASN1_OBJECT *pubkeyoid = NULL; + + X509_get0_signature(&psig, &sigalg, x); + if(sigalg) { + i2a_ASN1_OBJECT(mem, sigalg->algorithm); + push_certinfo("Signature Algorithm", i); + } + + xpubkey = X509_get_X509_PUBKEY(x); + if(xpubkey) { + X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey); + if(pubkeyoid) { + i2a_ASN1_OBJECT(mem, pubkeyoid); push_certinfo("Public Key Algorithm", i); } } + X509V3_ext(data, i, X509_get0_extensions(x)); } #else @@ -3154,7 +3576,7 @@ static CURLcode get_cert_chain(struct connectdata *conn, const BIGNUM *e; RSA_get0_key(rsa, &n, &e, NULL); - BN_print(mem, n); + BIO_printf(mem, "%d", BN_num_bits(n)); push_certinfo("RSA Public Key", i); print_pubkey_BN(rsa, n, i); print_pubkey_BN(rsa, e, i); @@ -3279,7 +3701,6 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, if(len1 < 1) break; /* failed */ - /* https://www.openssl.org/docs/crypto/buffer.html */ buff1 = temp = malloc(len1); if(!buff1) break; /* failed */ @@ -3301,7 +3722,6 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1); } while(0); - /* https://www.openssl.org/docs/crypto/buffer.html */ if(buff1) free(buff1); @@ -3329,16 +3749,21 @@ static CURLcode servercert(struct connectdata *conn, char error_buffer[256]=""; char buffer[2048]; const char *ptr; +#ifndef CURL_DISABLE_PROXY long * const certverifyresult = SSL_IS_PROXY() ? &data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult; +#else + long * const certverifyresult = &data->set.ssl.certverifyresult; +#endif BIO *mem = BIO_new(BIO_s_mem()); + struct ssl_backend_data *backend = connssl->backend; if(data->set.ssl.certinfo) /* we've been asked to gather certificate info! */ (void)get_cert_chain(conn, connssl); - BACKEND->server_cert = SSL_get_peer_certificate(BACKEND->handle); - if(!BACKEND->server_cert) { + backend->server_cert = SSL_get_peer_certificate(backend->handle); + if(!backend->server_cert) { BIO_free(mem); if(!strict) return CURLE_OK; @@ -3349,19 +3774,19 @@ static CURLcode servercert(struct connectdata *conn, infof(data, "%s certificate:\n", SSL_IS_PROXY() ? "Proxy" : "Server"); - rc = x509_name_oneline(X509_get_subject_name(BACKEND->server_cert), + rc = x509_name_oneline(X509_get_subject_name(backend->server_cert), buffer, sizeof(buffer)); infof(data, " subject: %s\n", rc?"[NONE]":buffer); #ifndef CURL_DISABLE_VERBOSE_STRINGS { long len; - ASN1_TIME_print(mem, X509_get0_notBefore(BACKEND->server_cert)); + ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " start date: %.*s\n", len, ptr); (void)BIO_reset(mem); - ASN1_TIME_print(mem, X509_get0_notAfter(BACKEND->server_cert)); + ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " expire date: %.*s\n", len, ptr); (void)BIO_reset(mem); @@ -3371,15 +3796,15 @@ static CURLcode servercert(struct connectdata *conn, BIO_free(mem); if(SSL_CONN_CONFIG(verifyhost)) { - result = verifyhost(conn, BACKEND->server_cert); + result = verifyhost(conn, backend->server_cert); if(result) { - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; return result; } } - rc = x509_name_oneline(X509_get_issuer_name(BACKEND->server_cert), + rc = x509_name_oneline(X509_get_issuer_name(backend->server_cert), buffer, sizeof(buffer)); if(rc) { if(strict) @@ -3393,27 +3818,32 @@ static CURLcode servercert(struct connectdata *conn, deallocating the certificate. */ /* e.g. match issuer name with provided issuer certificate */ - if(SSL_SET_OPTION(issuercert)) { - fp = BIO_new(BIO_s_file()); - if(fp == NULL) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; - return CURLE_OUT_OF_MEMORY; - } + if(SSL_SET_OPTION(issuercert) || SSL_SET_OPTION(issuercert_blob)) { + if(SSL_SET_OPTION(issuercert_blob)) + fp = BIO_new_mem_buf(SSL_SET_OPTION(issuercert_blob)->data, + (int)SSL_SET_OPTION(issuercert_blob)->len); + else { + fp = BIO_new(BIO_s_file()); + if(fp == NULL) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + X509_free(backend->server_cert); + backend->server_cert = NULL; + return CURLE_OUT_OF_MEMORY; + } - if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) { - if(strict) - failf(data, "SSL: Unable to open issuer cert (%s)", - SSL_SET_OPTION(issuercert)); - BIO_free(fp); - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; - return CURLE_SSL_ISSUER_ERROR; + if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) { + if(strict) + failf(data, "SSL: Unable to open issuer cert (%s)", + SSL_SET_OPTION(issuercert)); + BIO_free(fp); + X509_free(backend->server_cert); + backend->server_cert = NULL; + return CURLE_SSL_ISSUER_ERROR; + } } issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL); @@ -3423,19 +3853,19 @@ static CURLcode servercert(struct connectdata *conn, SSL_SET_OPTION(issuercert)); BIO_free(fp); X509_free(issuer); - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } - if(X509_check_issued(issuer, BACKEND->server_cert) != X509_V_OK) { + if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) { if(strict) failf(data, "SSL: Certificate issuer check failed (%s)", SSL_SET_OPTION(issuercert)); BIO_free(fp); X509_free(issuer); - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } @@ -3445,7 +3875,7 @@ static CURLcode servercert(struct connectdata *conn, X509_free(issuer); } - lerr = *certverifyresult = SSL_get_verify_result(BACKEND->handle); + lerr = *certverifyresult = SSL_get_verify_result(backend->handle); if(*certverifyresult != X509_V_OK) { if(SSL_CONN_CONFIG(verifypeer)) { @@ -3470,8 +3900,8 @@ static CURLcode servercert(struct connectdata *conn, if(SSL_CONN_CONFIG(verifystatus)) { result = verifystatus(conn, connssl); if(result) { - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; return result; } } @@ -3484,13 +3914,13 @@ static CURLcode servercert(struct connectdata *conn, ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; if(!result && ptr) { - result = pkp_pin_peer_pubkey(data, BACKEND->server_cert, ptr); + result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr); if(result) failf(data, "SSL: public key does not match pinned public key!"); } - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; connssl->connecting_state = ssl_connect_done; return result; @@ -3531,7 +3961,6 @@ static CURLcode ossl_connect_common(struct connectdata *conn, struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; - time_t timeout_ms; int what; /* check if the connection has already been established */ @@ -3542,7 +3971,7 @@ static CURLcode ossl_connect_common(struct connectdata *conn, if(ssl_connect_1 == connssl->connecting_state) { /* Find out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { /* no need to continue if time already is up */ @@ -3560,7 +3989,7 @@ static CURLcode ossl_connect_common(struct connectdata *conn, ssl_connect_2_writing == connssl->connecting_state) { /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { /* no need to continue if time already is up */ @@ -3657,14 +4086,15 @@ static bool Curl_ossl_data_pending(const struct connectdata *conn, int connindex) { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex]; - if(connssl->backend->handle && SSL_pending(connssl->backend->handle)) return TRUE; - - if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) - return TRUE; - +#ifndef CURL_DISABLE_PROXY + { + const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex]; + if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) + return TRUE; + } +#endif return FALSE; } @@ -3684,14 +4114,15 @@ static ssize_t ossl_send(struct connectdata *conn, int memlen; int rc; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - rc = SSL_write(BACKEND->handle, mem, memlen); + rc = SSL_write(backend->handle, mem, memlen); if(rc <= 0) { - err = SSL_get_error(BACKEND->handle, rc); + err = SSL_get_error(backend->handle, rc); switch(err) { case SSL_ERROR_WANT_READ: @@ -3702,18 +4133,33 @@ static ssize_t ossl_send(struct connectdata *conn, *curlcode = CURLE_AGAIN; return -1; case SSL_ERROR_SYSCALL: - failf(conn->data, "SSL_write() returned SYSCALL, errno = %d", - SOCKERRNO); - *curlcode = CURLE_SEND_ERROR; - return -1; + { + int sockerr = SOCKERRNO; + sslerror = ERR_get_error(); + if(sslerror) + ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); + else if(sockerr) + Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + else { + strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer)); + error_buffer[sizeof(error_buffer) - 1] = '\0'; + } + failf(conn->data, OSSL_PACKAGE " SSL_write: %s, errno %d", + error_buffer, sockerr); + *curlcode = CURLE_SEND_ERROR; + return -1; + } case SSL_ERROR_SSL: /* A failure in the SSL library occurred, usually a protocol error. The OpenSSL error queue contains more information on the error. */ sslerror = ERR_get_error(); if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL && ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET && - conn->ssl[sockindex].state == ssl_connection_complete && - conn->proxy_ssl[sockindex].state == ssl_connection_complete) { + conn->ssl[sockindex].state == ssl_connection_complete +#ifndef CURL_DISABLE_PROXY + && conn->proxy_ssl[sockindex].state == ssl_connection_complete +#endif + ) { char ver[120]; Curl_ossl_version(ver, 120); failf(conn->data, "Error: %s does not support double SSL tunneling.", @@ -3746,21 +4192,25 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */ ssize_t nread; int buffsize; struct ssl_connect_data *connssl = &conn->ssl[num]; + struct ssl_backend_data *backend = connssl->backend; ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - nread = (ssize_t)SSL_read(BACKEND->handle, buf, buffsize); + nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); if(nread <= 0) { /* failed SSL_read */ - int err = SSL_get_error(BACKEND->handle, (int)nread); + int err = SSL_get_error(backend->handle, (int)nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ break; case SSL_ERROR_ZERO_RETURN: /* no more data */ /* close_notify alert */ - connclose(conn, "TLS close_notify"); + if(num == FIRSTSOCKET) + /* mark the connection for close if it is indeed the control + connection */ + connclose(conn, "TLS close_notify"); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: @@ -3775,14 +4225,44 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */ if((nread < 0) || sslerror) { /* If the return code was negative or there actually is an error in the queue */ + int sockerr = SOCKERRNO; + if(sslerror) + ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); + else if(sockerr && err == SSL_ERROR_SYSCALL) + Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + else { + strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer)); + error_buffer[sizeof(error_buffer) - 1] = '\0'; + } failf(conn->data, OSSL_PACKAGE " SSL_read: %s, errno %d", - (sslerror ? - ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)) : - SSL_ERROR_to_str(err)), - SOCKERRNO); + error_buffer, sockerr); + *curlcode = CURLE_RECV_ERROR; + return -1; + } + /* For debug builds be a little stricter and error on any + SSL_ERROR_SYSCALL. For example a server may have closed the connection + abruptly without a close_notify alert. For compatibility with older + peers we don't do this by default. #4624 + + We can use this to gauge how many users may be affected, and + if it goes ok eventually transition to allow in dev and release with + the newest OpenSSL: #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) */ +#ifdef DEBUGBUILD + if(err == SSL_ERROR_SYSCALL) { + int sockerr = SOCKERRNO; + if(sockerr) + Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + else { + msnprintf(error_buffer, sizeof(error_buffer), + "Connection closed abruptly"); + } + failf(conn->data, OSSL_PACKAGE " SSL_read: %s, errno %d" + " (Fatal because this is a curl debug build)", + error_buffer, sockerr); *curlcode = CURLE_RECV_ERROR; return -1; } +#endif } } return nread; @@ -3790,13 +4270,35 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */ static size_t Curl_ossl_version(char *buffer, size_t size) { -#ifdef OPENSSL_IS_BORINGSSL +#ifdef LIBRESSL_VERSION_NUMBER +#if LIBRESSL_VERSION_NUMBER < 0x2070100fL + return msnprintf(buffer, size, "%s/%lx.%lx.%lx", + OSSL_PACKAGE, + (LIBRESSL_VERSION_NUMBER>>28)&0xf, + (LIBRESSL_VERSION_NUMBER>>20)&0xff, + (LIBRESSL_VERSION_NUMBER>>12)&0xff); +#else /* OpenSSL_version() first appeared in LibreSSL 2.7.1 */ + char *p; + int count; + const char *ver = OpenSSL_version(OPENSSL_VERSION); + const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */ + if(Curl_strncasecompare(ver, expected, sizeof(expected) - 1)) { + ver += sizeof(expected) - 1; + } + count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver); + for(p = buffer; *p; ++p) { + if(ISSPACE(*p)) + *p = '_'; + } + return count; +#endif +#elif defined(OPENSSL_IS_BORINGSSL) return msnprintf(buffer, size, OSSL_PACKAGE); #elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING) return msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING)); #else - /* not BoringSSL and not using OpenSSL_version */ + /* not LibreSSL, BoringSSL and not using OpenSSL_version */ char sub[3]; unsigned long ssleay_value; @@ -3881,7 +4383,7 @@ static CURLcode Curl_ossl_sha256sum(const unsigned char *tmp, /* input */ unsigned int len = 0; (void) unused; - mdctx = EVP_MD_CTX_create(); + mdctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL); EVP_DigestUpdate(mdctx, tmp, tmplen); EVP_DigestFinal_ex(mdctx, sha256sum, &len); @@ -3904,8 +4406,9 @@ static void *Curl_ossl_get_internals(struct ssl_connect_data *connssl, CURLINFO info) { /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ + struct ssl_backend_data *backend = connssl->backend; return info == CURLINFO_TLS_SESSION ? - (void *)BACKEND->ctx : (void *)BACKEND->handle; + (void *)backend->ctx : (void *)backend->handle; } const struct Curl_ssl Curl_ssl_openssl = { diff --git a/Utilities/cmcurl/lib/vtls/polarssl.c b/Utilities/cmcurl/lib/vtls/polarssl.c deleted file mode 100644 index 7ea26b44257..00000000000 --- a/Utilities/cmcurl/lib/vtls/polarssl.c +++ /dev/null @@ -1,931 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2019, Daniel Stenberg, , et al. - * Copyright (C) 2010 - 2011, Hoi-Ho Chan, - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.haxx.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. - * - ***************************************************************************/ - -/* - * Source file for all PolarSSL-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - * - */ - -#include "curl_setup.h" - -#ifdef USE_POLARSSL -#include -#include -#include -#include -#include -#include - -#if POLARSSL_VERSION_NUMBER < 0x01030000 -#error too old PolarSSL -#endif - -#include -#include -#include - -#include "urldata.h" -#include "sendf.h" -#include "inet_pton.h" -#include "polarssl.h" -#include "vtls.h" -#include "parsedate.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "strcase.h" -#include "polarssl_threadlock.h" -#include "multiif.h" -#include "curl_printf.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* See https://tls.mbed.org/discussions/generic/ - howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der -*/ -#define RSA_PUB_DER_MAX_BYTES (38 + 2 * POLARSSL_MPI_MAX_SIZE) -#define ECP_PUB_DER_MAX_BYTES (30 + 2 * POLARSSL_ECP_MAX_BYTES) - -#define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ - RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) - -struct ssl_backend_data { - ctr_drbg_context ctr_drbg; - entropy_context entropy; - ssl_context ssl; - int server_fd; - x509_crt cacert; - x509_crt clicert; - x509_crl crl; - rsa_context rsa; -}; - -#define BACKEND connssl->backend - -/* apply threading? */ -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) -#define THREADING_SUPPORT -#endif - -#ifndef POLARSSL_ERROR_C -#define error_strerror(x,y,z) -#endif /* POLARSSL_ERROR_C */ - - -#if defined(THREADING_SUPPORT) -static entropy_context entropy; - -static int entropy_init_initialized = 0; - -/* start of entropy_init_mutex() */ -static void entropy_init_mutex(entropy_context *ctx) -{ - /* lock 0 = entropy_init_mutex() */ - Curl_polarsslthreadlock_lock_function(0); - if(entropy_init_initialized == 0) { - entropy_init(ctx); - entropy_init_initialized = 1; - } - Curl_polarsslthreadlock_unlock_function(0); -} -/* end of entropy_init_mutex() */ - -/* start of entropy_func_mutex() */ -static int entropy_func_mutex(void *data, unsigned char *output, size_t len) -{ - int ret; - /* lock 1 = entropy_func_mutex() */ - Curl_polarsslthreadlock_lock_function(1); - ret = entropy_func(data, output, len); - Curl_polarsslthreadlock_unlock_function(1); - - return ret; -} -/* end of entropy_func_mutex() */ - -#endif /* THREADING_SUPPORT */ - -/* Define this to enable lots of debugging for PolarSSL */ -#undef POLARSSL_DEBUG - -#ifdef POLARSSL_DEBUG -static void polarssl_debug(void *context, int level, const char *line) -{ - struct Curl_easy *data = NULL; - - if(!context) - return; - - data = (struct Curl_easy *)context; - - infof(data, "%s", line); - (void) level; -} -#else -#endif - -/* ALPN for http2? */ -#ifdef POLARSSL_SSL_ALPN -# define HAS_ALPN -#endif - -static Curl_recv polarssl_recv; -static Curl_send polarssl_send; - -static CURLcode polarssl_version_from_curl(int *polarver, long ssl_version) -{ - switch(ssl_version) { - case CURL_SSLVERSION_TLSv1_0: - *polarver = SSL_MINOR_VERSION_1; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1: - *polarver = SSL_MINOR_VERSION_2; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2: - *polarver = SSL_MINOR_VERSION_3; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3: - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - -static CURLcode -set_ssl_version_min_max(struct connectdata *conn, int sockindex) -{ - struct Curl_easy *data = conn->data; - struct ssl_connect_data* connssl = &conn->ssl[sockindex]; - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - int ssl_min_ver = SSL_MINOR_VERSION_1; - int ssl_max_ver = SSL_MINOR_VERSION_1; - CURLcode result = CURLE_OK; - - switch(ssl_version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - ssl_version = CURL_SSLVERSION_TLSv1_0; - break; - } - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - break; - } - - result = polarssl_version_from_curl(&ssl_min_ver, ssl_version); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - result = polarssl_version_from_curl(&ssl_max_ver, ssl_version_max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; - } - - ssl_set_min_version(&BACKEND->ssl, SSL_MAJOR_VERSION_3, ssl_min_ver); - ssl_set_max_version(&BACKEND->ssl, SSL_MAJOR_VERSION_3, ssl_max_ver); - - return result; -} - -static CURLcode -polarssl_connect_step1(struct connectdata *conn, - int sockindex) -{ - struct Curl_easy *data = conn->data; - struct ssl_connect_data* connssl = &conn->ssl[sockindex]; - const char *capath = SSL_CONN_CONFIG(CApath); - const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : - conn->host.name; - const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; - int ret = -1; - char errorbuf[128]; - errorbuf[0] = 0; - - /* PolarSSL only supports SSLv3 and TLSv1 */ - if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) { - failf(data, "PolarSSL does not support SSLv2"); - return CURLE_SSL_CONNECT_ERROR; - } - -#ifdef THREADING_SUPPORT - entropy_init_mutex(&entropy); - - if((ret = ctr_drbg_init(&BACKEND->ctr_drbg, entropy_func_mutex, &entropy, - NULL, 0)) != 0) { - error_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", - -ret, errorbuf); - } -#else - entropy_init(&BACKEND->entropy); - - if((ret = ctr_drbg_init(&BACKEND->ctr_drbg, entropy_func, &BACKEND->entropy, - NULL, 0)) != 0) { - error_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", - -ret, errorbuf); - } -#endif /* THREADING_SUPPORT */ - - /* Load the trusted CA */ - memset(&BACKEND->cacert, 0, sizeof(x509_crt)); - - if(SSL_CONN_CONFIG(CAfile)) { - ret = x509_crt_parse_file(&BACKEND->cacert, - SSL_CONN_CONFIG(CAfile)); - - if(ret<0) { - error_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading ca cert file %s - PolarSSL: (-0x%04X) %s", - SSL_CONN_CONFIG(CAfile), -ret, errorbuf); - - if(SSL_CONN_CONFIG(verifypeer)) - return CURLE_SSL_CACERT_BADFILE; - } - } - - if(capath) { - ret = x509_crt_parse_path(&BACKEND->cacert, capath); - - if(ret<0) { - error_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading ca cert path %s - PolarSSL: (-0x%04X) %s", - capath, -ret, errorbuf); - - if(SSL_CONN_CONFIG(verifypeer)) - return CURLE_SSL_CACERT_BADFILE; - } - } - - /* Load the client certificate */ - memset(&BACKEND->clicert, 0, sizeof(x509_crt)); - - if(SSL_SET_OPTION(cert)) { - ret = x509_crt_parse_file(&BACKEND->clicert, - SSL_SET_OPTION(cert)); - - if(ret) { - error_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading client cert file %s - PolarSSL: (-0x%04X) %s", - SSL_SET_OPTION(cert), -ret, errorbuf); - - return CURLE_SSL_CERTPROBLEM; - } - } - - /* Load the client private key */ - if(SSL_SET_OPTION(key)) { - pk_context pk; - pk_init(&pk); - ret = pk_parse_keyfile(&pk, SSL_SET_OPTION(key), - SSL_SET_OPTION(key_passwd)); - if(ret == 0 && !pk_can_do(&pk, POLARSSL_PK_RSA)) - ret = POLARSSL_ERR_PK_TYPE_MISMATCH; - if(ret == 0) - rsa_copy(&BACKEND->rsa, pk_rsa(pk)); - else - rsa_free(&BACKEND->rsa); - pk_free(&pk); - - if(ret) { - error_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading private key %s - PolarSSL: (-0x%04X) %s", - SSL_SET_OPTION(key), -ret, errorbuf); - - return CURLE_SSL_CERTPROBLEM; - } - } - - /* Load the CRL */ - memset(&BACKEND->crl, 0, sizeof(x509_crl)); - - if(SSL_SET_OPTION(CRLfile)) { - ret = x509_crl_parse_file(&BACKEND->crl, - SSL_SET_OPTION(CRLfile)); - - if(ret) { - error_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading CRL file %s - PolarSSL: (-0x%04X) %s", - SSL_SET_OPTION(CRLfile), -ret, errorbuf); - - return CURLE_SSL_CRL_BADFILE; - } - } - - infof(data, "PolarSSL: Connecting to %s:%d\n", hostname, port); - - if(ssl_init(&BACKEND->ssl)) { - failf(data, "PolarSSL: ssl_init failed"); - return CURLE_SSL_CONNECT_ERROR; - } - - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - ssl_set_min_version(&BACKEND->ssl, SSL_MAJOR_VERSION_3, - SSL_MINOR_VERSION_1); - break; - case CURL_SSLVERSION_SSLv3: - ssl_set_min_version(&BACKEND->ssl, SSL_MAJOR_VERSION_3, - SSL_MINOR_VERSION_0); - ssl_set_max_version(&BACKEND->ssl, SSL_MAJOR_VERSION_3, - SSL_MINOR_VERSION_0); - infof(data, "PolarSSL: Forced min. SSL Version to be SSLv3\n"); - break; - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - CURLcode result = set_ssl_version_min_max(conn, sockindex); - if(result != CURLE_OK) - return result; - break; - } - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - ssl_set_endpoint(&BACKEND->ssl, SSL_IS_CLIENT); - ssl_set_authmode(&BACKEND->ssl, SSL_VERIFY_OPTIONAL); - - ssl_set_rng(&BACKEND->ssl, ctr_drbg_random, - &BACKEND->ctr_drbg); - ssl_set_bio(&BACKEND->ssl, - net_recv, &conn->sock[sockindex], - net_send, &conn->sock[sockindex]); - - ssl_set_ciphersuites(&BACKEND->ssl, ssl_list_ciphersuites()); - - /* Check if there's a cached ID we can/should use here! */ - if(SSL_SET_OPTION(primary.sessionid)) { - void *old_session = NULL; - - Curl_ssl_sessionid_lock(conn); - if(!Curl_ssl_getsessionid(conn, &old_session, NULL, sockindex)) { - ret = ssl_set_session(&BACKEND->ssl, old_session); - if(ret) { - Curl_ssl_sessionid_unlock(conn); - failf(data, "ssl_set_session returned -0x%x", -ret); - return CURLE_SSL_CONNECT_ERROR; - } - infof(data, "PolarSSL re-using session\n"); - } - Curl_ssl_sessionid_unlock(conn); - } - - ssl_set_ca_chain(&BACKEND->ssl, - &BACKEND->cacert, - &BACKEND->crl, - hostname); - - ssl_set_own_cert_rsa(&BACKEND->ssl, - &BACKEND->clicert, &BACKEND->rsa); - - if(ssl_set_hostname(&BACKEND->ssl, hostname)) { - /* 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. */ - failf(data, "couldn't set hostname in PolarSSL"); - return CURLE_SSL_CONNECT_ERROR; - } - -#ifdef HAS_ALPN - if(conn->bits.tls_enable_alpn) { - static const char *protocols[3]; - int cur = 0; - -#ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2) { - protocols[cur++] = NGHTTP2_PROTO_VERSION_ID; - infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); - } -#endif - - protocols[cur++] = ALPN_HTTP_1_1; - infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1); - - protocols[cur] = NULL; - - ssl_set_alpn_protocols(&BACKEND->ssl, protocols); - } -#endif - -#ifdef POLARSSL_DEBUG - ssl_set_dbg(&BACKEND->ssl, polarssl_debug, data); -#endif - - connssl->connecting_state = ssl_connect_2; - - return CURLE_OK; -} - -static CURLcode -polarssl_connect_step2(struct connectdata *conn, - int sockindex) -{ - int ret; - struct Curl_easy *data = conn->data; - struct ssl_connect_data* connssl = &conn->ssl[sockindex]; - char buffer[1024]; - const char * const pinnedpubkey = SSL_IS_PROXY() ? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : - data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; - - - char errorbuf[128]; - errorbuf[0] = 0; - - conn->recv[sockindex] = polarssl_recv; - conn->send[sockindex] = polarssl_send; - - ret = ssl_handshake(&BACKEND->ssl); - - switch(ret) { - case 0: - break; - - case POLARSSL_ERR_NET_WANT_READ: - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_OK; - - case POLARSSL_ERR_NET_WANT_WRITE: - connssl->connecting_state = ssl_connect_2_writing; - return CURLE_OK; - - default: - error_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "ssl_handshake returned - PolarSSL: (-0x%04X) %s", - -ret, errorbuf); - return CURLE_SSL_CONNECT_ERROR; - } - - infof(data, "PolarSSL: Handshake complete, cipher is %s\n", - ssl_get_ciphersuite(&BACKEND->ssl) ); - - ret = ssl_get_verify_result(&BACKEND->ssl); - - if(ret && SSL_CONN_CONFIG(verifypeer)) { - if(ret & BADCERT_EXPIRED) - failf(data, "Cert verify failed: BADCERT_EXPIRED"); - - if(ret & BADCERT_REVOKED) { - failf(data, "Cert verify failed: BADCERT_REVOKED"); - return CURLE_PEER_FAILED_VERIFICATION; - } - - if(ret & BADCERT_CN_MISMATCH) - failf(data, "Cert verify failed: BADCERT_CN_MISMATCH"); - - if(ret & BADCERT_NOT_TRUSTED) - failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); - - return CURLE_PEER_FAILED_VERIFICATION; - } - - if(ssl_get_peer_cert(&(BACKEND->ssl))) { - /* If the session was resumed, there will be no peer certs */ - memset(buffer, 0, sizeof(buffer)); - - if(x509_crt_info(buffer, sizeof(buffer), (char *)"* ", - ssl_get_peer_cert(&(BACKEND->ssl))) != -1) - infof(data, "Dumping cert info:\n%s\n", buffer); - } - - /* adapted from mbedtls.c */ - if(pinnedpubkey) { - int size; - CURLcode result; - x509_crt *p; - unsigned char pubkey[PUB_DER_MAX_BYTES]; - const x509_crt *peercert; - - peercert = ssl_get_peer_cert(&BACKEND->ssl); - - if(!peercert || !peercert->raw.p || !peercert->raw.len) { - failf(data, "Failed due to missing peer certificate"); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - } - - p = calloc(1, sizeof(*p)); - - if(!p) - return CURLE_OUT_OF_MEMORY; - - x509_crt_init(p); - - /* Make a copy of our const peercert because pk_write_pubkey_der - needs a non-const key, for now. - https://github.com/ARMmbed/mbedtls/issues/396 */ - if(x509_crt_parse_der(p, peercert->raw.p, peercert->raw.len)) { - failf(data, "Failed copying peer certificate"); - x509_crt_free(p); - free(p); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - } - - size = pk_write_pubkey_der(&p->pk, pubkey, PUB_DER_MAX_BYTES); - - if(size <= 0) { - failf(data, "Failed copying public key from peer certificate"); - x509_crt_free(p); - free(p); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - } - - /* pk_write_pubkey_der writes data at the end of the buffer. */ - result = Curl_pin_peer_pubkey(data, - pinnedpubkey, - &pubkey[PUB_DER_MAX_BYTES - size], size); - if(result) { - x509_crt_free(p); - free(p); - return result; - } - - x509_crt_free(p); - free(p); - } - -#ifdef HAS_ALPN - if(conn->bits.tls_enable_alpn) { - const char *next_protocol = ssl_get_alpn_protocol(&BACKEND->ssl); - - if(next_protocol != NULL) { - infof(data, "ALPN, server accepted to use %s\n", next_protocol); - -#ifdef USE_NGHTTP2 - if(!strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID, - NGHTTP2_PROTO_VERSION_ID_LEN)) { - conn->negnpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) { - conn->negnpn = CURL_HTTP_VERSION_1_1; - } - } - else - infof(data, "ALPN, server did not agree to a protocol\n"); - Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } -#endif - - connssl->connecting_state = ssl_connect_3; - infof(data, "SSL connected\n"); - - return CURLE_OK; -} - -static CURLcode -polarssl_connect_step3(struct connectdata *conn, - int sockindex) -{ - CURLcode retcode = CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct Curl_easy *data = conn->data; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - - if(SSL_SET_OPTION(primary.sessionid)) { - int ret; - ssl_session *our_ssl_sessionid; - void *old_ssl_sessionid = NULL; - - our_ssl_sessionid = calloc(1, sizeof(ssl_session)); - if(!our_ssl_sessionid) - return CURLE_OUT_OF_MEMORY; - - ret = ssl_get_session(&BACKEND->ssl, our_ssl_sessionid); - if(ret) { - failf(data, "ssl_get_session returned -0x%x", -ret); - return CURLE_SSL_CONNECT_ERROR; - } - - /* If there's already a matching session in the cache, delete it */ - Curl_ssl_sessionid_lock(conn); - if(!Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL, sockindex)) - Curl_ssl_delsessionid(conn, old_ssl_sessionid); - - retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, 0, sockindex); - Curl_ssl_sessionid_unlock(conn); - if(retcode) { - free(our_ssl_sessionid); - failf(data, "failed to store ssl session"); - return retcode; - } - } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; -} - -static ssize_t polarssl_send(struct connectdata *conn, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - int ret = -1; - - ret = ssl_write(&BACKEND->ssl, - (unsigned char *)mem, len); - - if(ret < 0) { - *curlcode = (ret == POLARSSL_ERR_NET_WANT_WRITE) ? - CURLE_AGAIN : CURLE_SEND_ERROR; - ret = -1; - } - - return ret; -} - -static void Curl_polarssl_close(struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - rsa_free(&BACKEND->rsa); - x509_crt_free(&BACKEND->clicert); - x509_crt_free(&BACKEND->cacert); - x509_crl_free(&BACKEND->crl); - ssl_free(&BACKEND->ssl); -} - -static ssize_t polarssl_recv(struct connectdata *conn, - int num, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - struct ssl_connect_data *connssl = &conn->ssl[num]; - int ret = -1; - ssize_t len = -1; - - memset(buf, 0, buffersize); - ret = ssl_read(&BACKEND->ssl, (unsigned char *)buf, buffersize); - - if(ret <= 0) { - if(ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) - return 0; - - *curlcode = (ret == POLARSSL_ERR_NET_WANT_READ) ? - CURLE_AGAIN : CURLE_RECV_ERROR; - return -1; - } - - len = ret; - - return len; -} - -static void Curl_polarssl_session_free(void *ptr) -{ - ssl_session_free(ptr); - free(ptr); -} - -/* 1.3.10 was the first rebranded version. All new releases (in 1.3 branch and - higher) will be mbed TLS branded.. */ - -static size_t Curl_polarssl_version(char *buffer, size_t size) -{ - unsigned int version = version_get_number(); - return msnprintf(buffer, size, "%s/%d.%d.%d", - version >= 0x01030A00?"mbedTLS":"PolarSSL", - version>>24, (version>>16)&0xff, (version>>8)&0xff); -} - -static CURLcode -polarssl_connect_common(struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) -{ - CURLcode result; - struct Curl_easy *data = conn->data; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - long timeout_ms; - int what; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - result = polarssl_connect_step1(conn, sockindex); - if(result) - return result; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading || - connssl->connecting_state == ssl_connect_2_writing) { - - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0:timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - else { - /* timeout */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if - * this connection is part of a multi handle and this loop would - * execute again. This permits the owner of a multi handle to - * abort a connection attempt before step2 has completed while - * ensuring that a client using select() or epoll() will always - * have a valid fdset to wait on. - */ - result = polarssl_connect_step2(conn, sockindex); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return result; - - } /* repeat step2 until all transactions are done. */ - - if(ssl_connect_3 == connssl->connecting_state) { - result = polarssl_connect_step3(conn, sockindex); - if(result) - return result; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = polarssl_recv; - conn->send[sockindex] = polarssl_send; - *done = TRUE; - } - else - *done = FALSE; - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode Curl_polarssl_connect_nonblocking(struct connectdata *conn, - int sockindex, bool *done) -{ - return polarssl_connect_common(conn, sockindex, TRUE, done); -} - - -static CURLcode Curl_polarssl_connect(struct connectdata *conn, int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = polarssl_connect_common(conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -/* - * return 0 error initializing SSL - * return 1 SSL initialized successfully - */ -static int Curl_polarssl_init(void) -{ - return Curl_polarsslthreadlock_thread_setup(); -} - -static void Curl_polarssl_cleanup(void) -{ - (void)Curl_polarsslthreadlock_thread_cleanup(); -} - -static bool Curl_polarssl_data_pending(const struct connectdata *conn, - int sockindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - return ssl_get_bytes_avail(&BACKEND->ssl) != 0; -} - -static CURLcode Curl_polarssl_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len UNUSED_PARAM) -{ - (void)sha256len; - sha256(input, inputlen, sha256sum, 0); - return CURLE_OK; -} - -static void *Curl_polarssl_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - (void)info; - return &BACKEND->ssl; -} - -const struct Curl_ssl Curl_ssl_polarssl = { - { CURLSSLBACKEND_POLARSSL, "polarssl" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_PINNEDPUBKEY, - - sizeof(struct ssl_backend_data), - - Curl_polarssl_init, /* init */ - Curl_polarssl_cleanup, /* cleanup */ - Curl_polarssl_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ - Curl_polarssl_data_pending, /* data_pending */ - /* This might cause libcurl to use a weeker random! */ - Curl_none_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - Curl_polarssl_connect, /* connect */ - Curl_polarssl_connect_nonblocking, /* connect_nonblocking */ - Curl_polarssl_get_internals, /* get_internals */ - Curl_polarssl_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_polarssl_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - Curl_none_md5sum, /* md5sum */ - Curl_polarssl_sha256sum /* sha256sum */ -}; - -#endif /* USE_POLARSSL */ diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 0f6f734fdcd..1996526066f 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -7,7 +7,7 @@ * * Copyright (C) 2012 - 2016, Marc Hoersken, * Copyright (C) 2012, Mark Salisbury, - * Copyright (C) 2012 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,16 +27,6 @@ * but vtls.c should ever call or use these functions. */ -/* - * Based upon the PolarSSL implementation in polarssl.c and polarssl.h: - * Copyright (C) 2010, 2011, Hoi-Ho Chan, - * - * Based upon the CyaSSL implementation in cyassl.c and cyassl.h: - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. - * - * Thanks for code and inspiration! - */ - #include "curl_setup.h" #ifdef USE_SCHANNEL @@ -49,10 +39,11 @@ #include "schannel.h" #include "vtls.h" +#include "strcase.h" #include "sendf.h" #include "connect.h" /* for the connect timeout */ #include "strerror.h" -#include "select.h" /* for the socket readyness */ +#include "select.h" /* for the socket readiness */ #include "inet_pton.h" /* for IP addr SNI check */ #include "curl_multibyte.h" #include "warnless.h" @@ -61,7 +52,7 @@ #include "multiif.h" #include "system_win32.h" - /* The last #include file should be: */ +/* The last #include file should be: */ #include "curl_memory.h" #include "memdebug.h" @@ -179,25 +170,25 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn) long i = ssl_version; switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - break; + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_DEFAULT: + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; + break; } for(; i <= (ssl_version_max >> 16); ++i) { switch(i) { - case CURL_SSLVERSION_TLSv1_0: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_1: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_2: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "schannel: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; + case CURL_SSLVERSION_TLSv1_0: + schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_1: + schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_2: + schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_3: + failf(data, "schannel: TLS 1.3 is not yet supported"); + return CURLE_SSL_CONNECT_ERROR; } } return CURLE_OK; @@ -205,9 +196,9 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn) /*longest is 26, buffer is slightly bigger*/ #define LONGEST_ALG_ID 32 -#define CIPHEROPTION(X) \ -if(strcmp(#X, tmp) == 0) \ - return X +#define CIPHEROPTION(X) \ + if(strcmp(#X, tmp) == 0) \ + return X static int get_alg_id_by_name(char *name) @@ -283,11 +274,11 @@ get_alg_id_by_name(char *name) #ifdef CALG_HMAC CIPHEROPTION(CALG_HMAC); #endif -#if !defined(__W32API_MAJOR_VERSION) || \ - !defined(__W32API_MINOR_VERSION) || \ - defined(__MINGW64_VERSION_MAJOR) || \ - (__W32API_MAJOR_VERSION > 5) || \ - ((__W32API_MAJOR_VERSION == 5) && (__W32API_MINOR_VERSION > 0)) +#if !defined(__W32API_MAJOR_VERSION) || \ + !defined(__W32API_MINOR_VERSION) || \ + defined(__MINGW64_VERSION_MAJOR) || \ + (__W32API_MAJOR_VERSION > 5) || \ + ((__W32API_MAJOR_VERSION == 5) && (__W32API_MINOR_VERSION > 0)) /* CALG_TLS1PRF has a syntax error in MinGW's w32api up to version 5.0, see https://osdn.net/projects/mingw/ticket/38391 */ CIPHEROPTION(CALG_TLS1PRF); @@ -349,7 +340,7 @@ set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers) if(startCur) startCur++; } - schannel_cred->palgSupportedAlgs = algIds; + schannel_cred->palgSupportedAlgs = algIds; schannel_cred->cSupportedAlgs = algCount; return CURLE_OK; } @@ -434,8 +425,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) #endif TCHAR *host_name; CURLcode result; +#ifndef CURL_DISABLE_PROXY char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + char * const hostname = conn->host.name; +#endif DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n", @@ -443,20 +438,20 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) if(Curl_verify_windows_version(5, 1, PLATFORM_WINNT, VERSION_LESS_THAN_EQUAL)) { - /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and - algorithms that may not be supported by all servers. */ - infof(data, "schannel: Windows version is old and may not be able to " - "connect to some servers due to lack of SNI, algorithms, etc.\n"); + /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and + algorithms that may not be supported by all servers. */ + infof(data, "schannel: Windows version is old and may not be able to " + "connect to some servers due to lack of SNI, algorithms, etc.\n"); } #ifdef HAS_ALPN /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above. Also it doesn't seem to be supported for Wine, see curl bug #983. */ BACKEND->use_alpn = conn->bits.tls_enable_alpn && - !GetProcAddress(GetModuleHandleA("ntdll"), - "wine_get_version") && - Curl_verify_windows_version(6, 3, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL); + !GetProcAddress(GetModuleHandleA("ntdll"), + "wine_get_version") && + Curl_verify_windows_version(6, 3, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL); #else BACKEND->use_alpn = false; #endif @@ -530,8 +525,15 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) DEBUGF(infof(data, "schannel: disabled server certificate revocation " "checks\n")); } + else if(data->set.ssl.revoke_best_effort) { + schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN; + + DEBUGF(infof(data, "schannel: ignore revocation offline errors")); + } else { schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; + DEBUGF(infof(data, "schannel: checking server certificate revocation\n")); } @@ -554,20 +556,16 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) switch(conn->ssl_config.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: - schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | - SP_PROT_TLS1_1_CLIENT | - SP_PROT_TLS1_2_CLIENT; - break; case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: - { - result = set_ssl_version_min_max(&schannel_cred, conn); - if(result != CURLE_OK) - return result; - break; - } + { + result = set_ssl_version_min_max(&schannel_cred, conn); + if(result != CURLE_OK) + return result; + break; + } case CURL_SSLVERSION_SSLv3: schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; break; @@ -590,68 +588,186 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) #ifdef HAS_CLIENT_CERT_PATH /* client certificate */ - if(data->set.ssl.cert) { - DWORD cert_store_name; - TCHAR *cert_store_path; - TCHAR *cert_thumbprint_str; + if(data->set.ssl.cert || data->set.ssl.cert_blob) { + DWORD cert_store_name = 0; + TCHAR *cert_store_path = NULL; + TCHAR *cert_thumbprint_str = NULL; CRYPT_HASH_BLOB cert_thumbprint; BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; - HCERTSTORE cert_store; + HCERTSTORE cert_store = NULL; + FILE *fInCert = NULL; + void *certdata = NULL; + size_t certsize = 0; + bool blob = data->set.ssl.cert_blob != NULL; + TCHAR *cert_path = NULL; + if(blob) { + certdata = data->set.ssl.cert_blob->data; + certsize = data->set.ssl.cert_blob->len; + } + else { + cert_path = curlx_convert_UTF8_to_tchar(data->set.ssl.cert); + if(!cert_path) + return CURLE_OUT_OF_MEMORY; - TCHAR *cert_path = Curl_convert_UTF8_to_tchar(data->set.ssl.cert); - if(!cert_path) - return CURLE_OUT_OF_MEMORY; + result = get_cert_location(cert_path, &cert_store_name, + &cert_store_path, &cert_thumbprint_str); - result = get_cert_location(cert_path, &cert_store_name, - &cert_store_path, &cert_thumbprint_str); - if(result != CURLE_OK) { - failf(data, "schannel: Failed to get certificate location for %s", - cert_path); - Curl_unicodefree(cert_path); - return result; + if(result && (data->set.ssl.cert[0]!='\0')) + fInCert = fopen(data->set.ssl.cert, "rb"); + + if(result && !fInCert) { + failf(data, "schannel: Failed to get certificate location" + " or file for %s", + data->set.ssl.cert); + curlx_unicodefree(cert_path); + return result; + } } - cert_store = - CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, - (HCRYPTPROV)NULL, - CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, - cert_store_path); - if(!cert_store) { - failf(data, "schannel: Failed to open cert store %x %s, " - "last error is %x", - cert_store_name, cert_store_path, GetLastError()); - free(cert_store_path); - Curl_unicodefree(cert_path); + if((fInCert || blob) && (data->set.ssl.cert_type) && + (!strcasecompare(data->set.ssl.cert_type, "P12"))) { + failf(data, "schannel: certificate format compatibility error " + " for %s", + blob ? "(memory blob)" : data->set.ssl.cert); + curlx_unicodefree(cert_path); return CURLE_SSL_CERTPROBLEM; } - free(cert_store_path); - cert_thumbprint.pbData = cert_thumbprint_data; - cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; + if(fInCert || blob) { + /* Reading a .P12 or .pfx file, like the example at bottom of + https://social.msdn.microsoft.com/Forums/windowsdesktop/ + en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 + */ + CRYPT_DATA_BLOB datablob; + WCHAR* pszPassword; + size_t pwd_len = 0; + int str_w_len = 0; + const char *cert_showfilename_error = blob ? + "(memory blob)" : data->set.ssl.cert; + curlx_unicodefree(cert_path); + if(fInCert) { + long cert_tell = 0; + bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; + if(continue_reading) + cert_tell = ftell(fInCert); + if(cert_tell < 0) + continue_reading = FALSE; + else + certsize = (size_t)cert_tell; + if(continue_reading) + continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; + if(continue_reading) + certdata = malloc(certsize + 1); + if((!certdata) || + ((int) fread(certdata, certsize, 1, fInCert) != 1)) + continue_reading = FALSE; + fclose(fInCert); + if(!continue_reading) { + failf(data, "schannel: Failed to read cert file %s", + data->set.ssl.cert); + free(certdata); + return CURLE_SSL_CERTPROBLEM; + } + } - if(!CryptStringToBinary(cert_thumbprint_str, CERT_THUMBPRINT_STR_LEN, - CRYPT_STRING_HEX, - cert_thumbprint_data, &cert_thumbprint.cbData, - NULL, NULL)) { - Curl_unicodefree(cert_path); - return CURLE_SSL_CERTPROBLEM; - } + /* Convert key-pair data to the in-memory certificate store */ + datablob.pbData = (BYTE*)certdata; + datablob.cbData = (DWORD)certsize; + + if(data->set.ssl.key_passwd != NULL) + pwd_len = strlen(data->set.ssl.key_passwd); + pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1)); + if(pszPassword) { + if(pwd_len > 0) + str_w_len = MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + data->set.ssl.key_passwd, (int)pwd_len, + pszPassword, (int)(pwd_len + 1)); + + if((str_w_len >= 0) && (str_w_len <= (int)pwd_len)) + pszPassword[str_w_len] = 0; + else + pszPassword[0] = 0; + + cert_store = PFXImportCertStore(&datablob, pszPassword, 0); + free(pszPassword); + } + if(!blob) + free(certdata); + if(cert_store == NULL) { + DWORD errorcode = GetLastError(); + if(errorcode == ERROR_INVALID_PASSWORD) + failf(data, "schannel: Failed to import cert file %s, " + "password is bad", + cert_showfilename_error); + else + failf(data, "schannel: Failed to import cert file %s, " + "last error is 0x%x", + cert_showfilename_error, errorcode); + return CURLE_SSL_CERTPROBLEM; + } - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_HASH, &cert_thumbprint, NULL); + client_certs[0] = CertFindCertificateInStore( + cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_ANY, NULL, NULL); - Curl_unicodefree(cert_path); + if(client_certs[0] == NULL) { + failf(data, "schannel: Failed to get certificate from file %s" + ", last error is 0x%x", + cert_showfilename_error, GetLastError()); + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } - if(client_certs[0]) { schannel_cred.cCreds = 1; schannel_cred.paCred = client_certs; } else { - /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ - return CURLE_SSL_CERTPROBLEM; - } + cert_store = + CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, + (HCRYPTPROV)NULL, + CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, + cert_store_path); + if(!cert_store) { + failf(data, "schannel: Failed to open cert store %x %s, " + "last error is 0x%x", + cert_store_name, cert_store_path, GetLastError()); + free(cert_store_path); + curlx_unicodefree(cert_path); + return CURLE_SSL_CERTPROBLEM; + } + free(cert_store_path); + + cert_thumbprint.pbData = cert_thumbprint_data; + cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; + + if(!CryptStringToBinary(cert_thumbprint_str, + CERT_THUMBPRINT_STR_LEN, + CRYPT_STRING_HEX, + cert_thumbprint_data, + &cert_thumbprint.cbData, + NULL, NULL)) { + curlx_unicodefree(cert_path); + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } + + client_certs[0] = CertFindCertificateInStore( + cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_HASH, &cert_thumbprint, NULL); + + curlx_unicodefree(cert_path); + if(client_certs[0]) { + schannel_cred.cCreds = 1; + schannel_cred.paCred = client_certs; + } + else { + /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ + CertCloseStore(cert_store, 0); + return CURLE_SSL_CERTPROBLEM; + } + } CertCloseStore(cert_store, 0); } #else @@ -675,7 +791,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) BACKEND->cred->refcount = 1; /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx - */ + */ sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, @@ -692,15 +808,15 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); Curl_safefree(BACKEND->cred); switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - return CURLE_OUT_OF_MEMORY; - case SEC_E_NO_CREDENTIALS: - case SEC_E_SECPKG_NOT_FOUND: - case SEC_E_NOT_OWNER: - case SEC_E_UNKNOWN_CREDENTIALS: - case SEC_E_INTERNAL_ERROR: - default: - return CURLE_SSL_CONNECT_ERROR; + case SEC_E_INSUFFICIENT_MEMORY: + return CURLE_OUT_OF_MEMORY; + case SEC_E_NO_CREDENTIALS: + case SEC_E_SECPKG_NOT_FOUND: + case SEC_E_NOT_OWNER: + case SEC_E_UNKNOWN_CREDENTIALS: + case SEC_E_INTERNAL_ERROR: + default: + return CURLE_SSL_CONNECT_ERROR; } } } @@ -722,7 +838,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) unsigned short* list_len = NULL; /* The first four bytes will be an unsigned int indicating number - of bytes of data in the rest of the the buffer. */ + of bytes of data in the rest of the buffer. */ extension_len = (unsigned int *)(&alpn_buffer[cur]); cur += sizeof(unsigned int); @@ -784,7 +900,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) return CURLE_OUT_OF_MEMORY; } - host_name = Curl_convert_UTF8_to_tchar(hostname); + host_name = curlx_convert_UTF8_to_tchar(hostname); if(!host_name) return CURLE_OUT_OF_MEMORY; @@ -801,35 +917,35 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) 0, &BACKEND->ctxt->ctxt_handle, &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); - Curl_unicodefree(host_name); + curlx_unicodefree(host_name); if(sspi_status != SEC_I_CONTINUE_NEEDED) { char buffer[STRERROR_LEN]; Curl_safefree(BACKEND->ctxt); switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - failf(data, "schannel: initial InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; - case SEC_E_WRONG_PRINCIPAL: - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - /* - case SEC_E_INVALID_HANDLE: - case SEC_E_INVALID_TOKEN: - case SEC_E_LOGON_DENIED: - case SEC_E_TARGET_UNKNOWN: - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - case SEC_E_INTERNAL_ERROR: - case SEC_E_NO_CREDENTIALS: - case SEC_E_UNSUPPORTED_FUNCTION: - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: - */ - default: - failf(data, "schannel: initial InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_SSL_CONNECT_ERROR; + case SEC_E_INSUFFICIENT_MEMORY: + failf(data, "schannel: initial InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_OUT_OF_MEMORY; + case SEC_E_WRONG_PRINCIPAL: + failf(data, "schannel: SNI or certificate check failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_PEER_FAILED_VERIFICATION; + /* + case SEC_E_INVALID_HANDLE: + case SEC_E_INVALID_TOKEN: + case SEC_E_LOGON_DENIED: + case SEC_E_TARGET_UNKNOWN: + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + case SEC_E_INTERNAL_ERROR: + case SEC_E_NO_CREDENTIALS: + case SEC_E_UNSUPPORTED_FUNCTION: + case SEC_E_APPLICATION_PROTOCOL_MISMATCH: + */ + default: + failf(data, "schannel: initial InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_SSL_CONNECT_ERROR; } } @@ -875,8 +991,12 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; bool doread; +#ifndef CURL_DISABLE_PROXY char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + char * const hostname = conn->host.name; +#endif const char *pubkey_ptr; doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; @@ -984,18 +1104,18 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) memcpy(inbuf[0].pvBuffer, BACKEND->encdata_buffer, BACKEND->encdata_offset); - host_name = Curl_convert_UTF8_to_tchar(hostname); + host_name = curlx_convert_UTF8_to_tchar(hostname); if(!host_name) return CURLE_OUT_OF_MEMORY; /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx - */ + */ sspi_status = s_pSecFn->InitializeSecurityContext( &BACKEND->cred->cred_handle, &BACKEND->ctxt->ctxt_handle, host_name, BACKEND->req_flags, 0, 0, &inbuf_desc, 0, NULL, &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); - Curl_unicodefree(host_name); + curlx_unicodefree(host_name); /* free buffer for received handshake data */ Curl_safefree(inbuf[0].pvBuffer); @@ -1050,29 +1170,29 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) else { char buffer[STRERROR_LEN]; switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; - case SEC_E_WRONG_PRINCIPAL: - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - /* - case SEC_E_INVALID_HANDLE: - case SEC_E_INVALID_TOKEN: - case SEC_E_LOGON_DENIED: - case SEC_E_TARGET_UNKNOWN: - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - case SEC_E_INTERNAL_ERROR: - case SEC_E_NO_CREDENTIALS: - case SEC_E_UNSUPPORTED_FUNCTION: - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: - */ - default: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_SSL_CONNECT_ERROR; + case SEC_E_INSUFFICIENT_MEMORY: + failf(data, "schannel: next InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_OUT_OF_MEMORY; + case SEC_E_WRONG_PRINCIPAL: + failf(data, "schannel: SNI or certificate check failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_PEER_FAILED_VERIFICATION; + /* + case SEC_E_INVALID_HANDLE: + case SEC_E_INVALID_TOKEN: + case SEC_E_LOGON_DENIED: + case SEC_E_TARGET_UNKNOWN: + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + case SEC_E_INTERNAL_ERROR: + case SEC_E_NO_CREDENTIALS: + case SEC_E_UNSUPPORTED_FUNCTION: + case SEC_E_APPLICATION_PROTOCOL_MISMATCH: + */ + default: + failf(data, "schannel: next InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_SSL_CONNECT_ERROR; } } @@ -1181,6 +1301,7 @@ struct Adder_args struct connectdata *conn; CURLcode result; int idx; + int certs_count; }; static bool @@ -1191,7 +1312,9 @@ add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg) if(valid_cert_encoding(ccert_context)) { const char *beg = (const char *) ccert_context->pbCertEncoded; const char *end = beg + ccert_context->cbCertEncoded; - args->result = Curl_extract_certinfo(args->conn, (args->idx)++, beg, end); + int insert_index = (args->certs_count - 1) - args->idx; + args->result = Curl_extract_certinfo(args->conn, insert_index, beg, end); + args->idx++; } return args->result == CURLE_OK; } @@ -1238,8 +1361,10 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) #ifdef HAS_ALPN if(BACKEND->use_alpn) { - sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, - SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result); + sspi_status = + s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_APPLICATION_PROTOCOL, + &alpn_result); if(sspi_status != SEC_E_OK) { failf(data, "schannel: failed to retrieve ALPN result"); @@ -1250,21 +1375,21 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) SecApplicationProtocolNegotiationStatus_Success) { infof(data, "schannel: ALPN, server accepted to use %.*s\n", - alpn_result.ProtocolIdSize, alpn_result.ProtocolId); + alpn_result.ProtocolIdSize, alpn_result.ProtocolId); #ifdef USE_NGHTTP2 if(alpn_result.ProtocolIdSize == NGHTTP2_PROTO_VERSION_ID_LEN && !memcmp(NGHTTP2_PROTO_VERSION_ID, alpn_result.ProtocolId, - NGHTTP2_PROTO_VERSION_ID_LEN)) { + NGHTTP2_PROTO_VERSION_ID_LEN)) { conn->negnpn = CURL_HTTP_VERSION_2; } else #endif - if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId, - ALPN_HTTP_1_1_LENGTH)) { - conn->negnpn = CURL_HTTP_VERSION_1_1; - } + if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH && + !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId, + ALPN_HTTP_1_1_LENGTH)) { + conn->negnpn = CURL_HTTP_VERSION_1_1; + } } else infof(data, "ALPN, server did not agree to a protocol\n"); @@ -1311,8 +1436,10 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) if(data->set.ssl.certinfo) { int certs_count = 0; - sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context); + sspi_status = + s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &ccert_context); if((sspi_status != SEC_E_OK) || (ccert_context == NULL)) { failf(data, "schannel: failed to retrieve remote cert context"); @@ -1326,6 +1453,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) struct Adder_args args; args.conn = conn; args.idx = 0; + args.certs_count = certs_count; traverse_cert_store(ccert_context, add_cert_to_certinfo, &args); result = args.result; } @@ -1347,7 +1475,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex, struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; - time_t timeout_ms; + timediff_t timeout_ms; int what; /* check if the connection has already been established */ @@ -1543,14 +1671,10 @@ schannel_send(struct connectdata *conn, int sockindex, /* send entire message or fail */ while(len > (size_t)written) { - ssize_t this_write; - time_t timeleft; + ssize_t this_write = 0; int what; - - this_write = 0; - - timeleft = Curl_timeleft(conn->data, NULL, FALSE); - if(timeleft < 0) { + timediff_t timeout_ms = Curl_timeleft(conn->data, NULL, FALSE); + if(timeout_ms < 0) { /* we already got the timeout */ failf(conn->data, "schannel: timed out sending data " "(bytes sent: %zd)", written); @@ -1558,8 +1682,9 @@ schannel_send(struct connectdata *conn, int sockindex, written = -1; break; } - - what = SOCKET_WRITABLE(conn->sock[sockindex], timeleft); + else if(!timeout_ms) + timeout_ms = TIMEDIFF_T_MAX; + what = SOCKET_WRITABLE(conn->sock[sockindex], timeout_ms); if(what < 0) { /* fatal error */ failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -1653,8 +1778,8 @@ schannel_recv(struct connectdata *conn, int sockindex, } else if(!len) { /* It's debatable what to return when !len. Regardless we can't return - immediately because there may be data to decrypt (in the case we want to - decrypt all encrypted cached data) so handle !len later in cleanup. + immediately because there may be data to decrypt (in the case we want to + decrypt all encrypted cached data) so handle !len later in cleanup. */ ; /* do nothing */ } @@ -1664,7 +1789,7 @@ schannel_recv(struct connectdata *conn, int sockindex, if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || BACKEND->encdata_length < min_encdata_length) { reallocated_length = BACKEND->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; + CURL_SCHANNEL_BUFFER_FREE_SIZE; if(reallocated_length < min_encdata_length) { reallocated_length = min_encdata_length; } @@ -1732,7 +1857,7 @@ schannel_recv(struct connectdata *conn, int sockindex, InitSecBufferDesc(&inbuf_desc, inbuf, 4); /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx - */ + */ sspi_status = s_pSecFn->DecryptMessage(&BACKEND->ctxt->ctxt_handle, &inbuf_desc, 0, NULL); @@ -1748,7 +1873,7 @@ schannel_recv(struct connectdata *conn, int sockindex, /* increase buffer in order to fit the received amount of data */ size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? - inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; + inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; if(BACKEND->decdata_length - BACKEND->decdata_offset < size || BACKEND->decdata_length < len) { /* increase internal decrypted data buffer */ @@ -1818,7 +1943,7 @@ schannel_recv(struct connectdata *conn, int sockindex, if(BACKEND->encdata_offset) { *err = CURLE_RECV_ERROR; infof(data, "schannel: can't renogotiate, " - "encrypted data available\n"); + "encrypted data available\n"); goto cleanup; } /* begin renegotiation */ @@ -1855,7 +1980,9 @@ schannel_recv(struct connectdata *conn, int sockindex, goto cleanup; } else { +#ifndef CURL_DISABLE_VERBOSE_STRINGS char buffer[STRERROR_LEN]; +#endif *err = CURLE_RECV_ERROR; infof(data, "schannel: failed to read data from server: %s\n", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); @@ -1871,17 +1998,20 @@ schannel_recv(struct connectdata *conn, int sockindex, "schannel: decrypted data buffer: offset %zu length %zu\n", BACKEND->decdata_offset, BACKEND->decdata_length)); -cleanup: + cleanup: /* Warning- there is no guarantee the encdata state is valid at this point */ DEBUGF(infof(data, "schannel: schannel_recv cleanup\n")); /* Error if the connection has closed without a close_notify. - Behavior here is a matter of debate. We don't want to be vulnerable to a - truncation attack however there's some browser precedent for ignoring the - close_notify for compatibility reasons. - Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't - return close_notify. In that case if the connection was closed we assume it - was graceful (close_notify) since there doesn't seem to be a way to tell. + + The behavior here is a matter of debate. We don't want to be vulnerable + to a truncation attack however there's some browser precedent for + ignoring the close_notify for compatibility reasons. + + Additionally, Windows 2000 (v5.0) is a special case since it seems it + doesn't return close_notify. In that case if the connection was closed we + assume it was graceful (close_notify) since there doesn't seem to be a + way to tell. */ if(len && !BACKEND->decdata_offset && BACKEND->recv_connection_closed && !BACKEND->recv_sspi_close_notify) { @@ -1898,7 +2028,7 @@ schannel_recv(struct connectdata *conn, int sockindex, /* Any error other than CURLE_AGAIN is an unrecoverable error. */ if(*err && *err != CURLE_AGAIN) - BACKEND->recv_unrecoverable_err = *err; + BACKEND->recv_unrecoverable_err = *err; size = len < BACKEND->decdata_offset ? len : BACKEND->decdata_offset; if(size) { @@ -1915,10 +2045,11 @@ schannel_recv(struct connectdata *conn, int sockindex, } if(!*err && !BACKEND->recv_connection_closed) - *err = CURLE_AGAIN; + *err = CURLE_AGAIN; - /* It's debatable what to return when !len. We could return whatever error we - got from decryption but instead we override here so the return is consistent. + /* It's debatable what to return when !len. We could return whatever error + we got from decryption but instead we override here so the return is + consistent. */ if(!len) *err = CURLE_OK; @@ -1984,8 +2115,12 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) */ struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; +#ifndef CURL_DISABLE_PROXY char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + char * const hostname = conn->host.name; +#endif DEBUGASSERT(data); @@ -2014,7 +2149,7 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); } - host_name = Curl_convert_UTF8_to_tchar(hostname); + host_name = curlx_convert_UTF8_to_tchar(hostname); if(!host_name) return CURLE_OUT_OF_MEMORY; @@ -2036,7 +2171,7 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); - Curl_unicodefree(host_name); + curlx_unicodefree(host_name); if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { /* send close message which is in output buffer */ @@ -2145,8 +2280,8 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, SECURITY_STATUS sspi_status; const char *x509_der; DWORD x509_der_len; - curl_X509certificate x509_parsed; - curl_asn1Element *pubkey; + struct Curl_X509certificate x509_parsed; + struct Curl_asn1Element *pubkey; sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, @@ -2162,7 +2297,7 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (pCertContextServer->cbCertEncoded > 0))) + (pCertContextServer->cbCertEncoded > 0))) break; x509_der = (const char *)pCertContextServer->pbCertEncoded; @@ -2211,7 +2346,7 @@ static void Curl_schannel_checksum(const unsigned char *input, memset(checksum, 0, checksumlen); if(!CryptAcquireContext(&hProv, NULL, NULL, provType, - CRYPT_VERIFYCONTEXT)) + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) return; /* failed */ do { @@ -2253,9 +2388,9 @@ static CURLcode Curl_schannel_md5sum(unsigned char *input, } static CURLcode Curl_schannel_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len) + size_t inputlen, + unsigned char *sha256sum, + size_t sha256len) { Curl_schannel_checksum(input, inputlen, sha256sum, sha256len, PROV_RSA_AES, CALG_SHA_256); diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index 5a09e969e8c..bdd7199e49f 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c @@ -7,7 +7,7 @@ * * Copyright (C) 2012 - 2016, Marc Hoersken, * Copyright (C) 2012, Mark Salisbury, - * Copyright (C) 2012 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -57,7 +57,7 @@ #define BEGIN_CERT "-----BEGIN CERTIFICATE-----" #define END_CERT "\n-----END CERTIFICATE-----" -typedef struct { +struct cert_chain_engine_config_win7 { DWORD cbSize; HCERTSTORE hRestrictedRoot; HCERTSTORE hRestrictedTrust; @@ -70,7 +70,7 @@ typedef struct { DWORD CycleDetectionModulus; HCERTSTORE hExclusiveRoot; HCERTSTORE hExclusiveTrustedPeople; -} CERT_CHAIN_ENGINE_CONFIG_WIN7, *PCERT_CHAIN_ENGINE_CONFIG_WIN7; +}; static int is_cr_or_lf(char c) { @@ -94,12 +94,13 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, int num_certs = 0; size_t END_CERT_LEN; - ca_file_tstr = Curl_convert_UTF8_to_tchar((char *)ca_file); + ca_file_tstr = curlx_convert_UTF8_to_tchar((char *)ca_file); if(!ca_file_tstr) { char buffer[STRERROR_LEN]; failf(data, "schannel: invalid path name for CA file '%s': %s", - ca_file, Curl_strerror(GetLastError(), buffer, sizeof(buffer))); + ca_file, + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } @@ -111,7 +112,7 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, */ ca_file_handle = CreateFile(ca_file_tstr, GENERIC_READ, - 0, + FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, @@ -120,7 +121,8 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, char buffer[STRERROR_LEN]; failf(data, "schannel: failed to open CA file '%s': %s", - ca_file, Curl_strerror(GetLastError(), buffer, sizeof(buffer))); + ca_file, + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } @@ -129,7 +131,8 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, char buffer[STRERROR_LEN]; failf(data, "schannel: failed to determine size of CA file '%s': %s", - ca_file, Curl_strerror(GetLastError(), buffer, sizeof(buffer))); + ca_file, + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } @@ -159,7 +162,8 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, char buffer[STRERROR_LEN]; failf(data, "schannel: failed to read from CA file '%s': %s", - ca_file, Curl_strerror(GetLastError(), buffer, sizeof(buffer))); + ca_file, + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } @@ -223,7 +227,7 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, "schannel: failed to extract certificate from CA file " "'%s': %s", ca_file, - Curl_strerror(GetLastError(), buffer, sizeof(buffer))); + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; more_certs = 0; } @@ -252,7 +256,8 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, "schannel: failed to add certificate from CA file '%s' " "to certificate store: %s", ca_file, - Curl_strerror(GetLastError(), buffer, sizeof(buffer))); + Curl_winapi_strerror(GetLastError(), buffer, + sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; more_certs = 0; } @@ -283,11 +288,138 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, CloseHandle(ca_file_handle); } Curl_safefree(ca_file_buffer); - Curl_unicodefree(ca_file_tstr); + curlx_unicodefree(ca_file_tstr); return result; } +/* + * Returns the number of characters necessary to populate all the host_names. + * If host_names is not NULL, populate it with all the host names. Each string + * in the host_names is null-terminated and the last string is double + * null-terminated. If no DNS names are found, a single null-terminated empty + * string is returned. + */ +static DWORD cert_get_name_string(struct Curl_easy *data, + CERT_CONTEXT *cert_context, + LPTSTR host_names, + DWORD length) +{ + DWORD actual_length = 0; + BOOL compute_content = FALSE; + CERT_INFO *cert_info = NULL; + CERT_EXTENSION *extension = NULL; + CRYPT_DECODE_PARA decode_para = {0, 0, 0}; + CERT_ALT_NAME_INFO *alt_name_info = NULL; + DWORD alt_name_info_size = 0; + BOOL ret_val = FALSE; + LPTSTR current_pos = NULL; + DWORD i; + + /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ + if(Curl_verify_windows_version(6, 2, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { +#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG + /* CertGetNameString will provide the 8-bit character string without + * any decoding */ + DWORD name_flags = + CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG; + actual_length = CertGetNameString(cert_context, + CERT_NAME_DNS_TYPE, + name_flags, + NULL, + host_names, + length); + return actual_length; +#endif + } + + compute_content = host_names != NULL && length != 0; + + /* Initialize default return values. */ + actual_length = 1; + if(compute_content) { + *host_names = '\0'; + } + + if(!cert_context) { + failf(data, "schannel: Null certificate context."); + return actual_length; + } + + cert_info = cert_context->pCertInfo; + if(!cert_info) { + failf(data, "schannel: Null certificate info."); + return actual_length; + } + + extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, + cert_info->cExtension, + cert_info->rgExtension); + if(!extension) { + failf(data, "schannel: CertFindExtension() returned no extension."); + return actual_length; + } + + decode_para.cbSize = sizeof(CRYPT_DECODE_PARA); + + ret_val = + CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + szOID_SUBJECT_ALT_NAME2, + extension->Value.pbData, + extension->Value.cbData, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + &alt_name_info, + &alt_name_info_size); + if(!ret_val) { + failf(data, + "schannel: CryptDecodeObjectEx() returned no alternate name " + "information."); + return actual_length; + } + + current_pos = host_names; + + /* Iterate over the alternate names and populate host_names. */ + for(i = 0; i < alt_name_info->cAltEntry; i++) { + const CERT_ALT_NAME_ENTRY *entry = &alt_name_info->rgAltEntry[i]; + wchar_t *dns_w = NULL; + size_t current_length = 0; + + if(entry->dwAltNameChoice != CERT_ALT_NAME_DNS_NAME) { + continue; + } + if(entry->pwszDNSName == NULL) { + infof(data, "schannel: Empty DNS name."); + continue; + } + current_length = wcslen(entry->pwszDNSName) + 1; + if(!compute_content) { + actual_length += (DWORD)current_length; + continue; + } + /* Sanity check to prevent buffer overrun. */ + if((actual_length + current_length) > length) { + failf(data, "schannel: Not enough memory to list all host names."); + break; + } + dns_w = entry->pwszDNSName; + /* pwszDNSName is in ia5 string format and hence doesn't contain any + * non-ascii characters. */ + while(*dns_w != '\0') { + *current_pos++ = (char)(*dns_w++); + } + *current_pos++ = '\0'; + actual_length += (DWORD)current_length; + } + if(compute_content) { + /* Last string has double null-terminator. */ + *current_pos = '\0'; + } + return actual_length; +} + static CURLcode verify_host(struct Curl_easy *data, CERT_CONTEXT *pCertContextServer, const char * const conn_hostname) @@ -298,21 +430,8 @@ static CURLcode verify_host(struct Curl_easy *data, DWORD len = 0; DWORD actual_len = 0; - /* CertGetNameString will provide the 8-bit character string without - * any decoding */ - DWORD name_flags = CERT_NAME_DISABLE_IE4_UTF8_FLAG; - -#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG - name_flags |= CERT_NAME_SEARCH_ALL_NAMES_FLAG; -#endif - /* Determine the size of the string needed for the cert hostname */ - len = CertGetNameString(pCertContextServer, - CERT_NAME_DNS_TYPE, - name_flags, - NULL, - NULL, - 0); + len = cert_get_name_string(data, pCertContextServer, NULL, 0); if(len == 0) { failf(data, "schannel: CertGetNameString() returned no " @@ -329,12 +448,8 @@ static CURLcode verify_host(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; goto cleanup; } - actual_len = CertGetNameString(pCertContextServer, - CERT_NAME_DNS_TYPE, - name_flags, - NULL, - (LPTSTR) cert_hostname_buff, - len); + actual_len = cert_get_name_string( + data, pCertContextServer, (LPTSTR)cert_hostname_buff, len); /* Sanity check */ if(actual_len != len) { @@ -361,7 +476,7 @@ static CURLcode verify_host(struct Curl_easy *data, * is acceptable since both values are assumed to use ASCII * (or some equivalent) encoding */ - cert_hostname = Curl_convert_tchar_to_UTF8( + cert_hostname = curlx_convert_tchar_to_UTF8( &cert_hostname_buff[cert_hostname_buff_index]); if(!cert_hostname) { result = CURLE_OUT_OF_MEMORY; @@ -393,7 +508,7 @@ static CURLcode verify_host(struct Curl_easy *data, result = CURLE_PEER_FAILED_VERIFICATION; } - Curl_unicodefree(cert_hostname); + curlx_unicodefree(cert_hostname); } } @@ -407,7 +522,7 @@ static CURLcode verify_host(struct Curl_easy *data, failf(data, "schannel: server certificate name verification failed"); cleanup: - Curl_unicodefree(cert_hostname_buff); + curlx_unicodefree(cert_hostname_buff); return result; } @@ -422,9 +537,13 @@ CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex) const CERT_CHAIN_CONTEXT *pChainContext = NULL; HCERTCHAINENGINE cert_chain_engine = NULL; HCERTSTORE trust_store = NULL; +#ifndef CURL_DISABLE_PROXY const char * const conn_hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + const char * const conn_hostname = conn->host.name; +#endif sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, @@ -460,7 +579,7 @@ CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex) if(!trust_store) { char buffer[STRERROR_LEN]; failf(data, "schannel: failed to create certificate store: %s", - Curl_strerror(GetLastError(), buffer, sizeof(buffer))); + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; } else { @@ -470,7 +589,7 @@ CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex) } if(result == CURLE_OK) { - CERT_CHAIN_ENGINE_CONFIG_WIN7 engine_config; + struct cert_chain_engine_config_win7 engine_config; BOOL create_engine_result; memset(&engine_config, 0, sizeof(engine_config)); @@ -489,7 +608,7 @@ CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex) char buffer[STRERROR_LEN]; failf(data, "schannel: failed to create certificate chain engine: %s", - Curl_strerror(GetLastError(), buffer, sizeof(buffer))); + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; } } @@ -512,7 +631,7 @@ CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex) &pChainContext)) { char buffer[STRERROR_LEN]; failf(data, "schannel: CertGetCertificateChain failed: %s", - Curl_strerror(GetLastError(), buffer, sizeof(buffer))); + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); pChainContext = NULL; result = CURLE_PEER_FAILED_VERIFICATION; } @@ -521,6 +640,15 @@ CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex) CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0]; DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; + + if(data->set.ssl.revoke_best_effort) { + /* Ignore errors when root certificates are missing the revocation + * list URL, or when the list could not be downloaded because the + * server is currently unreachable. */ + dwTrustErrorMask &= ~(DWORD)(CERT_TRUST_REVOCATION_STATUS_UNKNOWN | + CERT_TRUST_IS_OFFLINE_REVOCATION); + } + if(dwTrustErrorMask) { if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED) failf(data, "schannel: CertGetCertificateChain trust error" diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index 2fdf662a1dc..2627aff16a4 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012 - 2017, Nick Zitzmann, . - * Copyright (C) 2012 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -79,7 +79,7 @@ /* These macros mean "the following code is present to allow runtime backward compatibility with at least this cat or earlier": (You set this at build-time using the compiler command line option - "-mmacos-version-min.") */ + "-mmacosx-version-min.") */ #define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 #define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060 #define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 @@ -138,8 +138,6 @@ struct ssl_backend_data { size_t ssl_write_buffered_length; }; -#define BACKEND connssl->backend - /* pinned public key support tests */ /* version 1 supports macOS 10.12+ and iOS 10+ */ @@ -201,7 +199,8 @@ static OSStatus SocketRead(SSLConnectionRef connection, UInt8 *currData = (UInt8 *)data; /*int sock = *(int *)connection;*/ struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; - int sock = BACKEND->ssl_sockfd; + struct ssl_backend_data *backend = connssl->backend; + int sock = backend->ssl_sockfd; OSStatus rtn = noErr; size_t bytesRead; ssize_t rrtn; @@ -230,7 +229,7 @@ static OSStatus SocketRead(SSLConnectionRef connection, break; case EAGAIN: rtn = errSSLWouldBlock; - BACKEND->ssl_direction = false; + backend->ssl_direction = false; break; default: rtn = ioErr; @@ -261,7 +260,8 @@ static OSStatus SocketWrite(SSLConnectionRef connection, size_t bytesSent = 0; /*int sock = *(int *)connection;*/ struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; - int sock = BACKEND->ssl_sockfd; + struct ssl_backend_data *backend = connssl->backend; + int sock = backend->ssl_sockfd; ssize_t length; size_t dataLen = *dataLength; const UInt8 *dataPtr = (UInt8 *)data; @@ -281,7 +281,7 @@ static OSStatus SocketWrite(SSLConnectionRef connection, theErr = errno; if(theErr == EAGAIN) { ortn = errSSLWouldBlock; - BACKEND->ssl_direction = true; + backend->ssl_direction = true; } else { ortn = ioErr; @@ -1126,12 +1126,12 @@ static OSStatus CopyIdentityWithLabel(char *label, } static OSStatus CopyIdentityFromPKCS12File(const char *cPath, + const struct curl_blob *blob, const char *cPassword, SecIdentityRef *out_cert_and_key) { OSStatus status = errSecItemNotFound; - CFURLRef pkcs_url = CFURLCreateFromFileSystemRepresentation(NULL, - (const UInt8 *)cPath, strlen(cPath), false); + CFURLRef pkcs_url = NULL; CFStringRef password = cPassword ? CFStringCreateWithCString(NULL, cPassword, kCFStringEncodingUTF8) : NULL; CFDataRef pkcs_data = NULL; @@ -1140,8 +1140,26 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, /* These constants are documented as having first appeared in 10.6 but they raise linker errors when used on that cat for some reason. */ #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS - if(CFURLCreateDataAndPropertiesFromResource(NULL, pkcs_url, &pkcs_data, - NULL, NULL, &status)) { + bool resource_imported; + + if(blob) { + pkcs_data = CFDataCreate(kCFAllocatorDefault, + (const unsigned char *)blob->data, blob->len); + status = (pkcs_data != NULL) ? errSecSuccess : errSecAllocate; + resource_imported = (pkcs_data != NULL); + } + else { + pkcs_url = + CFURLCreateFromFileSystemRepresentation(NULL, + (const UInt8 *)cPath, + strlen(cPath), false); + resource_imported = + CFURLCreateDataAndPropertiesFromResource(NULL, + pkcs_url, &pkcs_data, + NULL, NULL, &status); + } + + if(resource_imported) { CFArrayRef items = NULL; /* On iOS SecPKCS12Import will never add the client certificate to the @@ -1164,7 +1182,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, * the Keychain. * * As this doesn't match iOS, and apps may not want to see their client - * certificate saved in the the user's keychain, we use SecItemImport + * certificate saved in the user's keychain, we use SecItemImport * with a NULL keychain to avoid importing it. * * This returns a SecCertificateRef from which we can construct a @@ -1219,7 +1237,8 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, #endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ if(password) CFRelease(password); - CFRelease(pkcs_url); + if(pkcs_url) + CFRelease(pkcs_url); return status; } @@ -1276,6 +1295,7 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) { struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; long ssl_version = SSL_CONN_CONFIG(version); long ssl_version_max = SSL_CONN_CONFIG(version_max); long max_supported_version_by_os; @@ -1326,30 +1346,30 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) return result; } - (void)SSLSetProtocolVersionMin(BACKEND->ssl_ctx, darwin_ver_min); - (void)SSLSetProtocolVersionMax(BACKEND->ssl_ctx, darwin_ver_max); + (void)SSLSetProtocolVersionMin(backend->ssl_ctx, darwin_ver_min); + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, darwin_ver_max); return result; } else { #if CURL_SUPPORT_MAC_10_8 long i = ssl_version; - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); for(; i <= (ssl_version_max >> 16); i++) { switch(i) { case CURL_SSLVERSION_TLSv1_0: - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol1, true); break; case CURL_SSLVERSION_TLSv1_1: - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol11, true); break; case CURL_SSLVERSION_TLSv1_2: - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol12, true); break; @@ -1373,9 +1393,12 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, struct Curl_easy *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile); + const struct curl_blob *ssl_cablob = NULL; const bool verifypeer = SSL_CONN_CONFIG(verifypeer); char * const ssl_cert = SSL_SET_OPTION(cert); + const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(cert_blob); const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; @@ -1395,10 +1418,10 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLCreateContext != NULL) { /* use the newer API if available */ - if(BACKEND->ssl_ctx) - CFRelease(BACKEND->ssl_ctx); - BACKEND->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); - if(!BACKEND->ssl_ctx) { + if(backend->ssl_ctx) + CFRelease(backend->ssl_ctx); + backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); + if(!backend->ssl_ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } @@ -1406,9 +1429,9 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, else { /* The old ST API does not exist under iOS, so don't compile it: */ #if CURL_SUPPORT_MAC_10_8 - if(BACKEND->ssl_ctx) - (void)SSLDisposeContext(BACKEND->ssl_ctx); - err = SSLNewContext(false, &(BACKEND->ssl_ctx)); + if(backend->ssl_ctx) + (void)SSLDisposeContext(backend->ssl_ctx); + err = SSLNewContext(false, &(backend->ssl_ctx)); if(err != noErr) { failf(data, "SSL: couldn't create a context: OSStatus %d", err); return CURLE_OUT_OF_MEMORY; @@ -1416,31 +1439,31 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, #endif /* CURL_SUPPORT_MAC_10_8 */ } #else - if(BACKEND->ssl_ctx) - (void)SSLDisposeContext(BACKEND->ssl_ctx); - err = SSLNewContext(false, &(BACKEND->ssl_ctx)); + if(backend->ssl_ctx) + (void)SSLDisposeContext(backend->ssl_ctx); + err = SSLNewContext(false, &(backend->ssl_ctx)); if(err != noErr) { failf(data, "SSL: couldn't create a context: OSStatus %d", err); return CURLE_OUT_OF_MEMORY; } #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - BACKEND->ssl_write_buffered_length = 0UL; /* reset buffered write length */ + backend->ssl_write_buffered_length = 0UL; /* reset buffered write length */ /* check to see if we've been told to use an explicit SSL/TLS version */ #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLSetProtocolVersionMax != NULL) { switch(conn->ssl_config.version) { case CURL_SSLVERSION_TLSv1: - (void)SSLSetProtocolVersionMin(BACKEND->ssl_ctx, kTLSProtocol1); + (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1); #if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 if(__builtin_available(macOS 10.13, iOS 11.0, *)) { - (void)SSLSetProtocolVersionMax(BACKEND->ssl_ctx, kTLSProtocol13); + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol13); } else { - (void)SSLSetProtocolVersionMax(BACKEND->ssl_ctx, kTLSProtocol12); + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); } #else - (void)SSLSetProtocolVersionMax(BACKEND->ssl_ctx, kTLSProtocol12); + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); #endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 */ break; @@ -1456,20 +1479,20 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, break; } case CURL_SSLVERSION_SSLv3: - err = SSLSetProtocolVersionMin(BACKEND->ssl_ctx, kSSLProtocol3); + err = SSLSetProtocolVersionMin(backend->ssl_ctx, kSSLProtocol3); if(err != noErr) { failf(data, "Your version of the OS does not support SSLv3"); return CURLE_SSL_CONNECT_ERROR; } - (void)SSLSetProtocolVersionMax(BACKEND->ssl_ctx, kSSLProtocol3); + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kSSLProtocol3); break; case CURL_SSLVERSION_SSLv2: - err = SSLSetProtocolVersionMin(BACKEND->ssl_ctx, kSSLProtocol2); + err = SSLSetProtocolVersionMin(backend->ssl_ctx, kSSLProtocol2); if(err != noErr) { failf(data, "Your version of the OS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } - (void)SSLSetProtocolVersionMax(BACKEND->ssl_ctx, kSSLProtocol2); + (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kSSLProtocol2); break; default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); @@ -1478,19 +1501,19 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, } else { #if CURL_SUPPORT_MAC_10_8 - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); switch(conn->ssl_config.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol1, true); - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol11, true); - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol12, true); break; @@ -1505,7 +1528,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, break; } case CURL_SSLVERSION_SSLv3: - err = SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + err = SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocol3, true); if(err != noErr) { @@ -1514,7 +1537,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, } break; case CURL_SSLVERSION_SSLv2: - err = SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + err = SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocol2, true); if(err != noErr) { @@ -1534,12 +1557,12 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, " SSL/TLS version"); return CURLE_SSL_CONNECT_ERROR; } - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, kSSLProtocolAll, false); + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); switch(conn->ssl_config.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: - (void)SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol1, true); break; @@ -1553,7 +1576,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, failf(data, "Your version of the OS does not support TLSv1.3"); return CURLE_SSL_CONNECT_ERROR; case CURL_SSLVERSION_SSLv2: - err = SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + err = SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocol2, true); if(err != noErr) { @@ -1562,7 +1585,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, } break; case CURL_SSLVERSION_SSLv3: - err = SSLSetProtocolVersionEnabled(BACKEND->ssl_ctx, + err = SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocol3, true); if(err != noErr) { @@ -1596,7 +1619,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, /* expects length prefixed preference ordered list of protocols in wire * format */ - err = SSLSetALPNProtocols(BACKEND->ssl_ctx, alpnArr); + err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr); if(err != noErr) infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d\n", err); @@ -1610,15 +1633,16 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, "Transport. The private key must be in the Keychain.\n"); } - if(ssl_cert) { + if(ssl_cert || ssl_cert_blob) { + bool is_cert_data = ssl_cert_blob != NULL; + bool is_cert_file = (!is_cert_data) && is_file(ssl_cert); SecIdentityRef cert_and_key = NULL; - bool is_cert_file = is_file(ssl_cert); /* User wants to authenticate with a client cert. Look for it: If we detect that this is a file on disk, then let's load it. Otherwise, assume that the user wants to use an identity loaded from the Keychain. */ - if(is_cert_file) { + if(is_cert_file || is_cert_data) { if(!SSL_SET_OPTION(cert_type)) infof(data, "WARNING: SSL: Certificate type not set, assuming " "PKCS#12 format.\n"); @@ -1627,7 +1651,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, infof(data, "WARNING: SSL: The Security framework only supports " "loading identities that are in PKCS#12 format.\n"); - err = CopyIdentityFromPKCS12File(ssl_cert, + err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob, SSL_SET_OPTION(key_passwd), &cert_and_key); } else @@ -1657,7 +1681,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, certs_c[0] = cert_and_key; certs = CFArrayCreate(NULL, (const void **)certs_c, 1L, &kCFTypeArrayCallBacks); - err = SSLSetCertificate(BACKEND->ssl_ctx, certs); + err = SSLSetCertificate(backend->ssl_ctx, certs); if(certs) CFRelease(certs); if(err != noErr) { @@ -1667,27 +1691,30 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, CFRelease(cert_and_key); } else { + const char *cert_showfilename_error = + is_cert_data ? "(memory blob)" : ssl_cert; + switch(err) { case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */ failf(data, "SSL: Incorrect password for the certificate \"%s\" " - "and its private key.", ssl_cert); + "and its private key.", cert_showfilename_error); break; case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */ failf(data, "SSL: Couldn't make sense of the data in the " "certificate \"%s\" and its private key.", - ssl_cert); + cert_showfilename_error); break; case -25260: /* errSecPassphraseRequired */ failf(data, "SSL The certificate \"%s\" requires a password.", - ssl_cert); + cert_showfilename_error); break; case errSecItemNotFound: failf(data, "SSL: Can't find the certificate \"%s\" and its private " - "key in the Keychain.", ssl_cert); + "key in the Keychain.", cert_showfilename_error); break; default: failf(data, "SSL: Can't load the certificate \"%s\" and its private " - "key: OSStatus %d", ssl_cert, err); + "key: OSStatus %d", cert_showfilename_error, err); break; } return CURLE_SSL_CERTPROBLEM; @@ -1719,8 +1746,9 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, #else if(SSLSetSessionOption != NULL) { #endif /* CURL_BUILD_MAC */ - bool break_on_auth = !conn->ssl_config.verifypeer || ssl_cafile; - err = SSLSetSessionOption(BACKEND->ssl_ctx, + bool break_on_auth = !conn->ssl_config.verifypeer || + ssl_cafile || ssl_cablob; + err = SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionBreakOnServerAuth, break_on_auth); if(err != noErr) { @@ -1730,7 +1758,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, } else { #if CURL_SUPPORT_MAC_10_8 - err = SSLSetEnableCertVerify(BACKEND->ssl_ctx, + err = SSLSetEnableCertVerify(backend->ssl_ctx, conn->ssl_config.verifypeer?true:false); if(err != noErr) { failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); @@ -1739,7 +1767,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, #endif /* CURL_SUPPORT_MAC_10_8 */ } #else - err = SSLSetEnableCertVerify(BACKEND->ssl_ctx, + err = SSLSetEnableCertVerify(backend->ssl_ctx, conn->ssl_config.verifypeer?true:false); if(err != noErr) { failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); @@ -1747,10 +1775,11 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, } #endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ - if(ssl_cafile && verifypeer) { - bool is_cert_file = is_file(ssl_cafile); + if((ssl_cafile || ssl_cablob) && verifypeer) { + bool is_cert_data = ssl_cablob != NULL; + bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile); - if(!is_cert_file) { + if(!(is_cert_file || is_cert_data)) { failf(data, "SSL: can't load CA certificate file %s", ssl_cafile); return CURLE_SSL_CACERT_BADFILE; } @@ -1760,7 +1789,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, * Both hostname check and SNI require SSLSetPeerDomainName(). * Also: the verifyhost setting influences SNI usage */ if(conn->ssl_config.verifyhost) { - err = SSLSetPeerDomainName(BACKEND->ssl_ctx, hostname, + err = SSLSetPeerDomainName(backend->ssl_ctx, hostname, strlen(hostname)); if(err != noErr) { @@ -1786,7 +1815,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, higher priority, but it's probably better that we not connect at all than to give the user a false sense of security if the server only supports insecure ciphers. (Note: We don't care about SSLv2-only ciphers.) */ - err = SSLGetNumberSupportedCiphers(BACKEND->ssl_ctx, &all_ciphers_count); + err = SSLGetNumberSupportedCiphers(backend->ssl_ctx, &all_ciphers_count); if(err != noErr) { failf(data, "SSL: SSLGetNumberSupportedCiphers() failed: OSStatus %d", err); @@ -1803,7 +1832,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, failf(data, "SSL: Failed to allocate memory for allowed ciphers"); return CURLE_OUT_OF_MEMORY; } - err = SSLGetSupportedCiphers(BACKEND->ssl_ctx, all_ciphers, + err = SSLGetSupportedCiphers(backend->ssl_ctx, all_ciphers, &all_ciphers_count); if(err != noErr) { Curl_safefree(all_ciphers); @@ -1890,7 +1919,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, break; } } - err = SSLSetEnabledCiphers(BACKEND->ssl_ctx, allowed_ciphers, + err = SSLSetEnabledCiphers(backend->ssl_ctx, allowed_ciphers, allowed_ciphers_count); Curl_safefree(all_ciphers); Curl_safefree(allowed_ciphers); @@ -1903,9 +1932,9 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, /* We want to enable 1/n-1 when using a CBC cipher unless the user specifically doesn't want us doing that: */ if(SSLSetSessionOption != NULL) { - SSLSetSessionOption(BACKEND->ssl_ctx, kSSLSessionOptionSendOneByteRecord, + SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord, !data->set.ssl.enable_beast); - SSLSetSessionOption(BACKEND->ssl_ctx, kSSLSessionOptionFalseStart, + SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart, data->set.ssl.falsestart); /* false start support */ } #endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ @@ -1919,7 +1948,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, if(!Curl_ssl_getsessionid(conn, (void **)&ssl_sessionid, &ssl_sessionid_len, sockindex)) { /* we got a session id, use it! */ - err = SSLSetPeerID(BACKEND->ssl_ctx, ssl_sessionid, ssl_sessionid_len); + err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); Curl_ssl_sessionid_unlock(conn); if(err != noErr) { failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); @@ -1937,7 +1966,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, verifypeer, SSL_CONN_CONFIG(verifyhost), hostname, port); ssl_sessionid_len = strlen(ssl_sessionid); - err = SSLSetPeerID(BACKEND->ssl_ctx, ssl_sessionid, ssl_sessionid_len); + err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); if(err != noErr) { Curl_ssl_sessionid_unlock(conn); failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); @@ -1954,7 +1983,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, } } - err = SSLSetIOFuncs(BACKEND->ssl_ctx, SocketRead, SocketWrite); + err = SSLSetIOFuncs(backend->ssl_ctx, SocketRead, SocketWrite); if(err != noErr) { failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; @@ -1964,8 +1993,8 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn, /* We need to store the FD in a constant memory address, because * SSLSetConnection() will not copy that address. I've found that * conn->sock[sockindex] may change on its own. */ - BACKEND->ssl_sockfd = sockfd; - err = SSLSetConnection(BACKEND->ssl_ctx, connssl); + backend->ssl_sockfd = sockfd; + err = SSLSetConnection(backend->ssl_ctx, connssl); if(err != noErr) { failf(data, "SSL: SSLSetConnection() failed: %d", err); return CURLE_SSL_CONNECT_ERROR; @@ -2111,8 +2140,8 @@ static int append_cert_to_array(struct Curl_easy *data, return CURLE_OK; } -static int verify_cert(const char *cafile, struct Curl_easy *data, - SSLContextRef ctx) +static CURLcode verify_cert(const char *cafile, struct Curl_easy *data, + SSLContextRef ctx) { int n = 0, rc; long res; @@ -2346,6 +2375,7 @@ sectransp_connect_step2(struct connectdata *conn, int sockindex) { struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; OSStatus err; SSLCipherSuite cipher; SSLProtocol protocol = 0; @@ -2357,12 +2387,12 @@ sectransp_connect_step2(struct connectdata *conn, int sockindex) || ssl_connect_2_writing == connssl->connecting_state); /* Here goes nothing: */ - err = SSLHandshake(BACKEND->ssl_ctx); + err = SSLHandshake(backend->ssl_ctx); if(err != noErr) { switch(err) { case errSSLWouldBlock: /* they're not done with us yet */ - connssl->connecting_state = BACKEND->ssl_direction ? + connssl->connecting_state = backend->ssl_direction ? ssl_connect_2_writing : ssl_connect_2_reading; return CURLE_OK; @@ -2370,10 +2400,10 @@ sectransp_connect_step2(struct connectdata *conn, int sockindex) Leopard's headers */ case -9841: if(SSL_CONN_CONFIG(CAfile) && SSL_CONN_CONFIG(verifypeer)) { - int res = verify_cert(SSL_CONN_CONFIG(CAfile), data, - BACKEND->ssl_ctx); - if(res != CURLE_OK) - return res; + CURLcode result = verify_cert(SSL_CONN_CONFIG(CAfile), data, + backend->ssl_ctx); + if(result) + return result; } /* the documentation says we need to call SSLHandshake() again */ return sectransp_connect_step2(conn, sockindex); @@ -2580,7 +2610,7 @@ sectransp_connect_step2(struct connectdata *conn, int sockindex) #ifdef SECTRANSP_PINNEDPUBKEY if(data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]) { - CURLcode result = pkp_pin_peer_pubkey(data, BACKEND->ssl_ctx, + CURLcode result = pkp_pin_peer_pubkey(data, backend->ssl_ctx, data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]); if(result) { failf(data, "SSL: public key does not match pinned public key!"); @@ -2590,8 +2620,8 @@ sectransp_connect_step2(struct connectdata *conn, int sockindex) #endif /* SECTRANSP_PINNEDPUBKEY */ /* Informational message */ - (void)SSLGetNegotiatedCipher(BACKEND->ssl_ctx, &cipher); - (void)SSLGetNegotiatedProtocolVersion(BACKEND->ssl_ctx, &protocol); + (void)SSLGetNegotiatedCipher(backend->ssl_ctx, &cipher); + (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol); switch(protocol) { case kSSLProtocol2: infof(data, "SSL 2.0 connection using %s\n", @@ -2631,7 +2661,7 @@ sectransp_connect_step2(struct connectdata *conn, int sockindex) if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { CFArrayRef alpnArr = NULL; CFStringRef chosenProtocol = NULL; - err = SSLCopyALPNProtocols(BACKEND->ssl_ctx, &alpnArr); + err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr); if(err == noErr && alpnArr && CFArrayGetCount(alpnArr) >= 1) chosenProtocol = CFArrayGetValueAtIndex(alpnArr, 0); @@ -2674,19 +2704,20 @@ show_verbose_server_cert(struct connectdata *conn, { struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; CFArrayRef server_certs = NULL; SecCertificateRef server_cert; OSStatus err; CFIndex i, count; SecTrustRef trust = NULL; - if(!BACKEND->ssl_ctx) + if(!backend->ssl_ctx) return; #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS #if CURL_BUILD_IOS #pragma unused(server_certs) - err = SSLCopyPeerTrust(BACKEND->ssl_ctx, &trust); + err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); /* For some reason, SSLCopyPeerTrust() can return noErr and yet return a null trust, so be on guard for that: */ if(err == noErr && trust) { @@ -2712,7 +2743,7 @@ show_verbose_server_cert(struct connectdata *conn, Lion or later. */ if(SecTrustEvaluateAsync != NULL) { #pragma unused(server_certs) - err = SSLCopyPeerTrust(BACKEND->ssl_ctx, &trust); + err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); /* For some reason, SSLCopyPeerTrust() can return noErr and yet return a null trust, so be on guard for that: */ if(err == noErr && trust) { @@ -2732,7 +2763,7 @@ show_verbose_server_cert(struct connectdata *conn, } else { #if CURL_SUPPORT_MAC_10_8 - err = SSLCopyPeerCertificates(BACKEND->ssl_ctx, &server_certs); + err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); /* Just in case SSLCopyPeerCertificates() returns null too... */ if(err == noErr && server_certs) { count = CFArrayGetCount(server_certs); @@ -2754,7 +2785,7 @@ show_verbose_server_cert(struct connectdata *conn, #endif /* CURL_BUILD_IOS */ #else #pragma unused(trust) - err = SSLCopyPeerCertificates(BACKEND->ssl_ctx, &server_certs); + err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); if(err == noErr) { count = CFArrayGetCount(server_certs); for(i = 0L ; i < count ; i++) { @@ -2805,7 +2836,6 @@ sectransp_connect_common(struct connectdata *conn, struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; - long timeout_ms; int what; /* check if the connection has already been established */ @@ -2816,7 +2846,7 @@ sectransp_connect_common(struct connectdata *conn, if(ssl_connect_1 == connssl->connecting_state) { /* Find out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { /* no need to continue if time already is up */ @@ -2834,7 +2864,7 @@ sectransp_connect_common(struct connectdata *conn, ssl_connect_2_writing == connssl->connecting_state) { /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { /* no need to continue if time already is up */ @@ -2852,7 +2882,7 @@ sectransp_connect_common(struct connectdata *conn, connssl->connecting_state?sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0:timeout_ms); + nonblocking ? 0 : timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -2933,34 +2963,36 @@ static CURLcode Curl_sectransp_connect(struct connectdata *conn, int sockindex) static void Curl_sectransp_close(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; - if(BACKEND->ssl_ctx) { - (void)SSLClose(BACKEND->ssl_ctx); + if(backend->ssl_ctx) { + (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLCreateContext != NULL) - CFRelease(BACKEND->ssl_ctx); + CFRelease(backend->ssl_ctx); #if CURL_SUPPORT_MAC_10_8 else - (void)SSLDisposeContext(BACKEND->ssl_ctx); + (void)SSLDisposeContext(backend->ssl_ctx); #endif /* CURL_SUPPORT_MAC_10_8 */ #else - (void)SSLDisposeContext(BACKEND->ssl_ctx); + (void)SSLDisposeContext(backend->ssl_ctx); #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - BACKEND->ssl_ctx = NULL; + backend->ssl_ctx = NULL; } - BACKEND->ssl_sockfd = 0; + backend->ssl_sockfd = 0; } static int Curl_sectransp_shutdown(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; struct Curl_easy *data = conn->data; ssize_t nread; int what; int rc; char buf[120]; - if(!BACKEND->ssl_ctx) + if(!backend->ssl_ctx) return 0; #ifndef CURL_DISABLE_FTP @@ -3033,11 +3065,12 @@ static size_t Curl_sectransp_version(char *buffer, size_t size) static int Curl_sectransp_check_cxn(struct connectdata *conn) { struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; + struct ssl_backend_data *backend = connssl->backend; OSStatus err; SSLSessionState state; - if(BACKEND->ssl_ctx) { - err = SSLGetSessionState(BACKEND->ssl_ctx, &state); + if(backend->ssl_ctx) { + err = SSLGetSessionState(backend->ssl_ctx, &state); if(err == noErr) return state == kSSLConnected || state == kSSLHandshake; return -1; @@ -3049,11 +3082,12 @@ static bool Curl_sectransp_data_pending(const struct connectdata *conn, int connindex) { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; + struct ssl_backend_data *backend = connssl->backend; OSStatus err; size_t buffer; - if(BACKEND->ssl_ctx) { /* SSL is in use */ - err = SSLGetBufferedReadSize(BACKEND->ssl_ctx, &buffer); + if(backend->ssl_ctx) { /* SSL is in use */ + err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); if(err == noErr) return buffer > 0UL; return false; @@ -3119,6 +3153,7 @@ static ssize_t sectransp_send(struct connectdata *conn, { /*struct Curl_easy *data = conn->data;*/ struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; size_t processed = 0UL; OSStatus err; @@ -3137,15 +3172,15 @@ static ssize_t sectransp_send(struct connectdata *conn, over again with no new data until it quits returning errSSLWouldBlock. */ /* Do we have buffered data to write from the last time we were called? */ - if(BACKEND->ssl_write_buffered_length) { + if(backend->ssl_write_buffered_length) { /* Write the buffered data: */ - err = SSLWrite(BACKEND->ssl_ctx, NULL, 0UL, &processed); + err = SSLWrite(backend->ssl_ctx, NULL, 0UL, &processed); switch(err) { case noErr: /* processed is always going to be 0 because we didn't write to the buffer, so return how much was written to the socket */ - processed = BACKEND->ssl_write_buffered_length; - BACKEND->ssl_write_buffered_length = 0UL; + processed = backend->ssl_write_buffered_length; + backend->ssl_write_buffered_length = 0UL; break; case errSSLWouldBlock: /* argh, try again */ *curlcode = CURLE_AGAIN; @@ -3158,13 +3193,13 @@ static ssize_t sectransp_send(struct connectdata *conn, } else { /* We've got new data to write: */ - err = SSLWrite(BACKEND->ssl_ctx, mem, len, &processed); + err = SSLWrite(backend->ssl_ctx, mem, len, &processed); if(err != noErr) { switch(err) { case errSSLWouldBlock: /* Data was buffered but not sent, we have to tell the caller to try sending again, and remember how much was buffered */ - BACKEND->ssl_write_buffered_length = len; + backend->ssl_write_buffered_length = len; *curlcode = CURLE_AGAIN; return -1L; default: @@ -3185,8 +3220,12 @@ static ssize_t sectransp_recv(struct connectdata *conn, { /*struct Curl_easy *data = conn->data;*/ struct ssl_connect_data *connssl = &conn->ssl[num]; + struct ssl_backend_data *backend = connssl->backend; size_t processed = 0UL; - OSStatus err = SSLRead(BACKEND->ssl_ctx, buf, buffersize, &processed); + OSStatus err; + + again: + err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed); if(err != noErr) { switch(err) { @@ -3207,6 +3246,16 @@ static ssize_t sectransp_recv(struct connectdata *conn, return -1L; break; + /* The below is errSSLPeerAuthCompleted; it's not defined in + Leopard's headers */ + case -9841: + if(SSL_CONN_CONFIG(CAfile) && SSL_CONN_CONFIG(verifypeer)) { + CURLcode result = verify_cert(SSL_CONN_CONFIG(CAfile), conn->data, + backend->ssl_ctx); + if(result) + return result; + } + goto again; default: failf(conn->data, "SSLRead() return error %d", err); *curlcode = CURLE_RECV_ERROR; @@ -3220,8 +3269,9 @@ static ssize_t sectransp_recv(struct connectdata *conn, static void *Curl_sectransp_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { + struct ssl_backend_data *backend = connssl->backend; (void)info; - return BACKEND->ssl_ctx; + return backend->ssl_ctx; } const struct Curl_ssl Curl_ssl_sectransp = { diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index a7452dcd537..c3a55fb1cbd 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -63,6 +63,7 @@ #include "warnless.h" #include "curl_base64.h" #include "curl_printf.h" +#include "strdup.h" /* The last #include files should be: */ #include "curl_memory.h" @@ -82,22 +83,62 @@ else \ dest->var = NULL; +#define CLONE_BLOB(var) \ + if(blobdup(&dest->var, source->var)) \ + return FALSE; + +static CURLcode blobdup(struct curl_blob **dest, + struct curl_blob *src) +{ + DEBUGASSERT(dest); + DEBUGASSERT(!*dest); + if(src) { + /* only if there's data to dupe! */ + struct curl_blob *d; + d = malloc(sizeof(struct curl_blob) + src->len); + if(!d) + return CURLE_OUT_OF_MEMORY; + d->len = src->len; + /* Always duplicate because the connection may survive longer than the + handle that passed in the blob. */ + d->flags = CURL_BLOB_COPY; + d->data = (void *)((char *)d + sizeof(struct curl_blob)); + memcpy(d->data, src->data, src->len); + *dest = d; + } + return CURLE_OK; +} + +/* returns TRUE if the blobs are identical */ +static bool blobcmp(struct curl_blob *first, struct curl_blob *second) +{ + if(!first && !second) /* both are NULL */ + return TRUE; + if(!first || !second) /* one is NULL */ + return FALSE; + if(first->len != second->len) /* different sizes */ + return FALSE; + return !memcmp(first->data, second->data, first->len); /* same data */ +} + bool -Curl_ssl_config_matches(struct ssl_primary_config* data, - struct ssl_primary_config* needle) +Curl_ssl_config_matches(struct ssl_primary_config *data, + struct ssl_primary_config *needle) { if((data->version == needle->version) && (data->version_max == needle->version_max) && (data->verifypeer == needle->verifypeer) && (data->verifyhost == needle->verifyhost) && (data->verifystatus == needle->verifystatus) && + blobcmp(data->cert_blob, needle->cert_blob) && Curl_safe_strcasecompare(data->CApath, needle->CApath) && Curl_safe_strcasecompare(data->CAfile, needle->CAfile) && Curl_safe_strcasecompare(data->clientcert, needle->clientcert) && Curl_safe_strcasecompare(data->random_file, needle->random_file) && Curl_safe_strcasecompare(data->egdsocket, needle->egdsocket) && Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) && - Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13)) + Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) && + Curl_safe_strcasecompare(data->pinned_key, needle->pinned_key)) return TRUE; return FALSE; @@ -114,6 +155,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, dest->verifystatus = source->verifystatus; dest->sessionid = source->sessionid; + CLONE_BLOB(cert_blob); CLONE_STRING(CApath); CLONE_STRING(CAfile); CLONE_STRING(clientcert); @@ -121,11 +163,12 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, CLONE_STRING(egdsocket); CLONE_STRING(cipher_list); CLONE_STRING(cipher_list13); + CLONE_STRING(pinned_key); return TRUE; } -void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc) +void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) { Curl_safefree(sslc->CApath); Curl_safefree(sslc->CAfile); @@ -134,6 +177,8 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc) Curl_safefree(sslc->egdsocket); Curl_safefree(sslc->cipher_list); Curl_safefree(sslc->cipher_list13); + Curl_safefree(sslc->pinned_key); + Curl_safefree(sslc->cert_blob); } #ifdef USE_SSL @@ -171,6 +216,9 @@ int Curl_ssl_init(void) return Curl_ssl->init(); } +#if defined(CURL_WITH_MULTI_SSL) +static const struct Curl_ssl Curl_ssl_multi; +#endif /* Global cleanup */ void Curl_ssl_cleanup(void) @@ -178,6 +226,9 @@ void Curl_ssl_cleanup(void) if(init_ssl) { /* only cleanup if we did a previous init */ Curl_ssl->cleanup(); +#if defined(CURL_WITH_MULTI_SSL) + Curl_ssl = &Curl_ssl_multi; +#endif init_ssl = FALSE; } } @@ -206,6 +257,7 @@ static bool ssl_prefs_check(struct Curl_easy *data) return TRUE; } +#ifndef CURL_DISABLE_PROXY static CURLcode ssl_connect_init_proxy(struct connectdata *conn, int sockindex) { @@ -229,17 +281,20 @@ ssl_connect_init_proxy(struct connectdata *conn, int sockindex) } return CURLE_OK; } +#endif CURLcode Curl_ssl_connect(struct connectdata *conn, int sockindex) { CURLcode result; +#ifndef CURL_DISABLE_PROXY if(conn->bits.proxy_ssl_connected[sockindex]) { result = ssl_connect_init_proxy(conn, sockindex); if(result) return result; } +#endif if(!ssl_prefs_check(conn->data)) return CURLE_SSL_CONNECT_ERROR; @@ -261,12 +316,13 @@ Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done) { CURLcode result; +#ifndef CURL_DISABLE_PROXY if(conn->bits.proxy_ssl_connected[sockindex]) { result = ssl_connect_init_proxy(conn, sockindex); if(result) return result; } - +#endif if(!ssl_prefs_check(conn->data)) return CURLE_SSL_CONNECT_ERROR; @@ -312,13 +368,21 @@ bool Curl_ssl_getsessionid(struct connectdata *conn, long *general_age; bool no_match = TRUE; +#ifndef CURL_DISABLE_PROXY const bool isProxy = CONNECT_PROXY_SSL(); struct ssl_primary_config * const ssl_config = isProxy ? &conn->proxy_ssl_config : &conn->ssl_config; - const char * const name = isProxy ? conn->http_proxy.host.name : - conn->host.name; + const char * const name = isProxy ? + conn->http_proxy.host.name : conn->host.name; int port = isProxy ? (int)conn->port : conn->remote_port; +#else + /* no proxy support */ + struct ssl_primary_config * const ssl_config = &conn->ssl_config; + const char * const name = conn->host.name; + int port = conn->remote_port; + (void)sockindex; +#endif *ssl_sessionid = NULL; DEBUGASSERT(SSL_SET_OPTION(primary.sessionid)); @@ -420,14 +484,23 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn, char *clone_conn_to_host; int conn_to_port; long *general_age; +#ifndef CURL_DISABLE_PROXY const bool isProxy = CONNECT_PROXY_SSL(); struct ssl_primary_config * const ssl_config = isProxy ? &conn->proxy_ssl_config : &conn->ssl_config; - + const char *hostname = isProxy ? conn->http_proxy.host.name : + conn->host.name; +#else + /* proxy support disabled */ + const bool isProxy = FALSE; + struct ssl_primary_config * const ssl_config = &conn->ssl_config; + const char *hostname = conn->host.name; + (void)sockindex; +#endif DEBUGASSERT(SSL_SET_OPTION(primary.sessionid)); - clone_host = strdup(isProxy ? conn->http_proxy.host.name : conn->host.name); + clone_host = strdup(hostname); if(!clone_host) return CURLE_OUT_OF_MEMORY; /* bail out */ @@ -486,6 +559,7 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn, store->scheme = conn->handler->scheme; if(!Curl_clone_primary_ssl_config(ssl_config, &store->ssl_config)) { + Curl_free_primary_ssl_config(&store->ssl_config); store->sessionid = NULL; /* let caller free sessionid */ free(clone_host); free(clone_conn_to_host); @@ -513,16 +587,12 @@ void Curl_ssl_close_all(struct Curl_easy *data) } #if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \ - defined(USE_SECTRANSP) || defined(USE_POLARSSL) || defined(USE_NSS) || \ - defined(USE_MBEDTLS) || defined(USE_CYASSL) -int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) + defined(USE_SECTRANSP) || defined(USE_NSS) || \ + defined(USE_MBEDTLS) || defined(USE_WOLFSSL) || defined(USE_BEARSSL) +int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks) { struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; - if(!numsocks) - return GETSOCK_BLANK; - if(connssl->connecting_state == ssl_connect_2_writing) { /* write mode */ socks[0] = conn->sock[FIRSTSOCKET]; @@ -538,12 +608,10 @@ int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks, } #else int Curl_ssl_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { (void)conn; (void)socks; - (void)numsocks; return GETSOCK_BLANK; } /* USE_OPENSSL || USE_GNUTLS || USE_SCHANNEL || USE_SECTRANSP || USE_NSS */ @@ -680,7 +748,7 @@ CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num) } /* - * 'value' is NOT a zero terminated string + * 'value' is NOT a null-terminated string */ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, int certnum, @@ -702,10 +770,10 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, /* sprintf the label and colon */ msnprintf(output, outlen, "%s:", label); - /* memcpy the value (it might not be zero terminated) */ + /* memcpy the value (it might not be null-terminated) */ memcpy(&output[labellen + 1], value, valuelen); - /* zero terminate the output */ + /* null-terminate the output */ output[labellen + 1 + valuelen] = 0; nl = Curl_slist_append_nodup(ci->certinfo[certnum], output); @@ -1080,7 +1148,7 @@ bool Curl_none_false_start(void) CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen, unsigned char *md5sum, size_t md5len UNUSED_PARAM) { - MD5_context *MD5pw; + struct MD5_context *MD5pw; (void)md5len; @@ -1172,8 +1240,8 @@ static const struct Curl_ssl Curl_ssl_multi = { const struct Curl_ssl *Curl_ssl = #if defined(CURL_WITH_MULTI_SSL) &Curl_ssl_multi; -#elif defined(USE_CYASSL) - &Curl_ssl_cyassl; +#elif defined(USE_WOLFSSL) + &Curl_ssl_wolfssl; #elif defined(USE_SECTRANSP) &Curl_ssl_sectransp; #elif defined(USE_GNUTLS) @@ -1186,19 +1254,19 @@ const struct Curl_ssl *Curl_ssl = &Curl_ssl_nss; #elif defined(USE_OPENSSL) &Curl_ssl_openssl; -#elif defined(USE_POLARSSL) - &Curl_ssl_polarssl; #elif defined(USE_SCHANNEL) &Curl_ssl_schannel; #elif defined(USE_MESALINK) &Curl_ssl_mesalink; +#elif defined(USE_BEARSSL) + &Curl_ssl_bearssl; #else #error "Missing struct Curl_ssl for selected SSL backend" #endif static const struct Curl_ssl *available_backends[] = { -#if defined(USE_CYASSL) - &Curl_ssl_cyassl, +#if defined(USE_WOLFSSL) + &Curl_ssl_wolfssl, #endif #if defined(USE_SECTRANSP) &Curl_ssl_sectransp, @@ -1218,14 +1286,14 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_OPENSSL) &Curl_ssl_openssl, #endif -#if defined(USE_POLARSSL) - &Curl_ssl_polarssl, -#endif #if defined(USE_SCHANNEL) &Curl_ssl_schannel, #endif #if defined(USE_MESALINK) &Curl_ssl_mesalink, +#endif +#if defined(USE_BEARSSL) + &Curl_ssl_bearssl, #endif NULL }; @@ -1234,7 +1302,7 @@ static size_t Curl_multissl_version(char *buffer, size_t size) { static const struct Curl_ssl *selected; static char backends[200]; - static size_t total; + static size_t backends_len; const struct Curl_ssl *current; current = Curl_ssl == &Curl_ssl_multi ? available_backends[0] : Curl_ssl; @@ -1246,27 +1314,32 @@ static size_t Curl_multissl_version(char *buffer, size_t size) selected = current; - for(i = 0; available_backends[i] && p < (end - 4); i++) { - if(i) - *(p++) = ' '; - if(selected != available_backends[i]) - *(p++) = '('; - p += available_backends[i]->version(p, end - p - 2); - if(selected != available_backends[i]) - *(p++) = ')'; + backends[0] = '\0'; + + for(i = 0; available_backends[i]; ++i) { + char vb[200]; + bool paren = (selected != available_backends[i]); + + if(available_backends[i]->version(vb, sizeof(vb))) { + p += msnprintf(p, end - p, "%s%s%s%s", (p != backends ? " " : ""), + (paren ? "(" : ""), vb, (paren ? ")" : "")); + } } - *p = '\0'; - total = p - backends; + + backends_len = p - backends; } - if(size > total) - memcpy(buffer, backends, total + 1); - else { - memcpy(buffer, backends, size - 1); + if(!size) + return 0; + + if(size <= backends_len) { + strncpy(buffer, backends, size - 1); buffer[size - 1] = '\0'; + return size - 1; } - return CURLMIN(size - 1, total); + strcpy(buffer, backends); + return backends_len; } static int multissl_init(const struct Curl_ssl *backend) diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index 2a87ca1f72a..bcc84441616 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -102,23 +102,17 @@ CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen, #include "gtls.h" /* GnuTLS versions */ #include "nssg.h" /* NSS versions */ #include "gskit.h" /* Global Secure ToolKit versions */ -#include "polarssl.h" /* PolarSSL versions */ -#include "cyassl.h" /* CyaSSL versions */ +#include "wolfssl.h" /* wolfSSL versions */ #include "schannel.h" /* Schannel SSPI version */ #include "sectransp.h" /* SecureTransport (Darwin) version */ #include "mbedtls.h" /* mbedTLS versions */ #include "mesalink.h" /* MesaLink versions */ +#include "bearssl.h" /* BearSSL versions */ #ifndef MAX_PINNED_PUBKEY_SIZE #define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */ #endif -#ifndef MD5_DIGEST_LENGTH -#ifndef LIBWOLFSSL_VERSION_HEX /* because WolfSSL borks this */ -#define MD5_DIGEST_LENGTH 16 /* fixed size */ -#endif -#endif - #ifndef CURL_SHA256_DIGEST_LENGTH #define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ #endif @@ -129,22 +123,28 @@ CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen, /* set of helper macros for the backends to access the correct fields. For the proxy or for the remote host - to properly support HTTPS proxy */ +#ifndef CURL_DISABLE_PROXY +#define SSL_IS_PROXY() \ + (CURLPROXY_HTTPS == conn->http_proxy.proxytype && \ + ssl_connection_complete != \ + conn->proxy_ssl[conn->sock[SECONDARYSOCKET] == \ + CURL_SOCKET_BAD ? FIRSTSOCKET : SECONDARYSOCKET].state) +#define SSL_SET_OPTION(var) \ + (SSL_IS_PROXY() ? data->set.proxy_ssl.var : data->set.ssl.var) +#define SSL_CONN_CONFIG(var) \ + (SSL_IS_PROXY() ? conn->proxy_ssl_config.var : conn->ssl_config.var) +#else +#define SSL_IS_PROXY() FALSE +#define SSL_SET_OPTION(var) data->set.ssl.var +#define SSL_CONN_CONFIG(var) conn->ssl_config.var +#endif -#define SSL_IS_PROXY() (CURLPROXY_HTTPS == conn->http_proxy.proxytype && \ - ssl_connection_complete != conn->proxy_ssl[conn->sock[SECONDARYSOCKET] == \ - CURL_SOCKET_BAD ? FIRSTSOCKET : SECONDARYSOCKET].state) -#define SSL_SET_OPTION(var) (SSL_IS_PROXY() ? data->set.proxy_ssl.var : \ - data->set.ssl.var) -#define SSL_CONN_CONFIG(var) (SSL_IS_PROXY() ? \ - conn->proxy_ssl_config.var : conn->ssl_config.var) - -bool Curl_ssl_config_matches(struct ssl_primary_config* data, - struct ssl_primary_config* needle); +bool Curl_ssl_config_matches(struct ssl_primary_config *data, + struct ssl_primary_config *needle); bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source, struct ssl_primary_config *dest); -void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc); -int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); +void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc); +int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks); int Curl_ssl_backend(void); @@ -263,7 +263,6 @@ bool Curl_ssl_false_start(void); #define Curl_ssl_send(a,b,c,d,e) -1 #define Curl_ssl_recv(a,b,c,d,e) -1 #define Curl_ssl_initsessions(x,y) CURLE_OK -#define Curl_ssl_version(x,y) 0 #define Curl_ssl_data_pending(x,y) 0 #define Curl_ssl_check_cxn(x) 0 #define Curl_ssl_free_certinfo(x) Curl_nop_stmt diff --git a/Utilities/cmcurl/lib/vtls/cyassl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c similarity index 63% rename from Utilities/cmcurl/lib/vtls/cyassl.c rename to Utilities/cmcurl/lib/vtls/wolfssl.c index 44a2bdda627..7b2a124e4f8 100644 --- a/Utilities/cmcurl/lib/vtls/cyassl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -21,31 +21,21 @@ ***************************************************************************/ /* - * Source file for all CyaSSL-specific code for the TLS/SSL layer. No code + * Source file for all wolfSSL specific code for the TLS/SSL layer. No code * but vtls.c should ever call or use these functions. * */ #include "curl_setup.h" -#ifdef USE_CYASSL +#ifdef USE_WOLFSSL #define WOLFSSL_OPTIONS_IGNORE_SYS -/* CyaSSL's version.h, which should contain only the version, should come -before all other CyaSSL includes and be immediately followed by build config -aka options.h. https://curl.haxx.se/mail/lib-2015-04/0069.html */ -#include -#if defined(HAVE_CYASSL_OPTIONS_H) && (LIBCYASSL_VERSION_HEX > 0x03004008) -#if defined(CYASSL_API) || defined(WOLFSSL_API) -/* Safety measure. If either is defined some API include was already included -and that's a problem since options.h hasn't been included yet. */ -#error "CyaSSL API was included before the CyaSSL build options." -#endif -#include -#endif +#include +#include /* To determine what functions are available we rely on one or both of: - - the user's options.h generated by CyaSSL/wolfSSL + - the user's options.h generated by wolfSSL - the symbols detected by curl's configure Since they are markedly different from one another, and one or the other may not be available, we do some checking below to bring things in sync. */ @@ -61,8 +51,8 @@ and that's a problem since options.h hasn't been included yet. */ options.h, but is only seen in >= 3.6.6 since that's when they started disabling SSLv3 by default. */ #ifndef WOLFSSL_ALLOW_SSLV3 -#if (LIBCYASSL_VERSION_HEX < 0x03006006) || \ - defined(HAVE_WOLFSSLV3_CLIENT_METHOD) +#if (LIBWOLFSSL_VERSION_HEX < 0x03006006) || \ + defined(HAVE_WOLFSSLV3_CLIENT_METHOD) #define WOLFSSL_ALLOW_SSLV3 #endif #endif @@ -73,6 +63,7 @@ and that's a problem since options.h hasn't been included yet. */ #include "sendf.h" #include "inet_pton.h" #include "vtls.h" +#include "keylog.h" #include "parsedate.h" #include "connect.h" /* for the connect timeout */ #include "select.h" @@ -81,33 +72,21 @@ and that's a problem since options.h hasn't been included yet. */ #include "curl_printf.h" #include "multiif.h" -#include -#include -#ifdef HAVE_CYASSL_ERROR_SSL_H -#include -#else -#include -#endif -#include -#include - -#include "cyassl.h" +#include +#include +#include +#include "wolfssl.h" /* The last #include files should be: */ #include "curl_memory.h" #include "memdebug.h" -#if LIBCYASSL_VERSION_HEX < 0x02007002 /* < 2.7.2 */ -#define CYASSL_MAX_ERROR_SZ 80 -#endif - /* KEEP_PEER_CERT is a product of the presence of build time symbol OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is in wolfSSL's settings.h, and the latter two are build time symbols in options.h. */ #ifndef KEEP_PEER_CERT -#if defined(HAVE_CYASSL_GET_PEER_CERTIFICATE) || \ - defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \ +#if defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \ (defined(OPENSSL_EXTRA) && !defined(NO_CERTS)) #define KEEP_PEER_CERT #endif @@ -118,11 +97,109 @@ struct ssl_backend_data { SSL* handle; }; -#define BACKEND connssl->backend +static Curl_recv wolfssl_recv; +static Curl_send wolfssl_send; + +#ifdef OPENSSL_EXTRA +/* + * Availability note: + * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in + * WolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that + * option is not set, then TLS 1.3 will not be logged. + * For TLS 1.2 and before, we use wolfSSL_get_keys(). + * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA + * (--enable-opensslextra or --enable-all). + */ +#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) +static int +wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret, + int secretSz, void *ctx) +{ + const char *label; + unsigned char client_random[SSL3_RANDOM_SIZE]; + (void)ctx; + + if(!ssl || !Curl_tls_keylog_enabled()) { + return 0; + } + + switch(id) { + case CLIENT_EARLY_TRAFFIC_SECRET: + label = "CLIENT_EARLY_TRAFFIC_SECRET"; + break; + case CLIENT_HANDSHAKE_TRAFFIC_SECRET: + label = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"; + break; + case SERVER_HANDSHAKE_TRAFFIC_SECRET: + label = "SERVER_HANDSHAKE_TRAFFIC_SECRET"; + break; + case CLIENT_TRAFFIC_SECRET: + label = "CLIENT_TRAFFIC_SECRET_0"; + break; + case SERVER_TRAFFIC_SECRET: + label = "SERVER_TRAFFIC_SECRET_0"; + break; + case EARLY_EXPORTER_SECRET: + label = "EARLY_EXPORTER_SECRET"; + break; + case EXPORTER_SECRET: + label = "EXPORTER_SECRET"; + break; + default: + return 0; + } -static Curl_recv cyassl_recv; -static Curl_send cyassl_send; + if(SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) { + /* Should never happen as wolfSSL_KeepArrays() was called before. */ + return 0; + } + Curl_tls_keylog_write(label, client_random, secret, secretSz); + return 0; +} +#endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */ + +static void +wolfssl_log_tls12_secret(SSL *ssl) +{ + unsigned char *ms, *sr, *cr; + unsigned int msLen, srLen, crLen, i, x = 0; + +#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */ + /* wolfSSL_GetVersion is available since 3.13, we use it instead of + * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or + * --enable-all). Failing to perform this check could result in an unusable + * key log line when TLS 1.3 is actually negotiated. */ + switch(wolfSSL_GetVersion(ssl)) { + case WOLFSSL_SSLV3: + case WOLFSSL_TLSV1: + case WOLFSSL_TLSV1_1: + case WOLFSSL_TLSV1_2: + break; + default: + /* TLS 1.3 does not use this mechanism, the "master secret" returned below + * is not directly usable. */ + return; + } +#endif + + if(SSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != SSL_SUCCESS) { + return; + } + + /* Check for a missing master secret and skip logging. That can happen if + * curl rejects the server certificate and aborts the handshake. + */ + for(i = 0; i < msLen; i++) { + x |= ms[i]; + } + if(x == 0) { + return; + } + + Curl_tls_keylog_write("CLIENT_RANDOM", cr, ms, msLen); +} +#endif /* OPENSSL_EXTRA */ static int do_file_type(const char *type) { @@ -140,12 +217,13 @@ static int do_file_type(const char *type) * layer and do all necessary magic. */ static CURLcode -cyassl_connect_step1(struct connectdata *conn, +wolfssl_connect_step1(struct connectdata *conn, int sockindex) { char *ciphers; struct Curl_easy *data = conn->data; - struct ssl_connect_data* connssl = &conn->ssl[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; SSL_METHOD* req_method = NULL; curl_socket_t sockfd = conn->sock[sockindex]; #ifdef HAVE_SNI @@ -159,7 +237,7 @@ cyassl_connect_step1(struct connectdata *conn, return CURLE_OK; if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) { - failf(data, "CyaSSL does not support to set maximum SSL/TLS version"); + failf(data, "wolfSSL does not support to set maximum SSL/TLS version"); return CURLE_SSL_CONNECT_ERROR; } @@ -167,11 +245,11 @@ cyassl_connect_step1(struct connectdata *conn, switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: -#if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ +#if LIBWOLFSSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ /* minimum protocol version is set later after the CTX object is created */ req_method = SSLv23_client_method(); #else - infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " + infof(data, "wolfSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " "TLS 1.0 is used exclusively\n"); req_method = TLSv1_client_method(); #endif @@ -182,7 +260,7 @@ cyassl_connect_step1(struct connectdata *conn, req_method = TLSv1_client_method(); use_sni(TRUE); #else - failf(data, "CyaSSL does not support TLS 1.0"); + failf(data, "wolfSSL does not support TLS 1.0"); return CURLE_NOT_BUILT_IN; #endif break; @@ -200,7 +278,7 @@ cyassl_connect_step1(struct connectdata *conn, use_sni(TRUE); break; #else - failf(data, "CyaSSL: TLS 1.3 is not yet supported"); + failf(data, "wolfSSL: TLS 1.3 is not yet supported"); return CURLE_SSL_CONNECT_ERROR; #endif case CURL_SSLVERSION_SSLv3: @@ -208,12 +286,12 @@ cyassl_connect_step1(struct connectdata *conn, req_method = SSLv3_client_method(); use_sni(FALSE); #else - failf(data, "CyaSSL does not support SSLv3"); + failf(data, "wolfSSL does not support SSLv3"); return CURLE_NOT_BUILT_IN; #endif break; case CURL_SSLVERSION_SSLv2: - failf(data, "CyaSSL does not support SSLv2"); + failf(data, "wolfSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); @@ -225,11 +303,11 @@ cyassl_connect_step1(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; } - if(BACKEND->ctx) - SSL_CTX_free(BACKEND->ctx); - BACKEND->ctx = SSL_CTX_new(req_method); + if(backend->ctx) + SSL_CTX_free(backend->ctx); + backend->ctx = SSL_CTX_new(req_method); - if(!BACKEND->ctx) { + if(!backend->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } @@ -237,19 +315,18 @@ cyassl_connect_step1(struct connectdata *conn, switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: -#if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ - /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever - minimum version of TLS was built in and at least TLS 1.0. For later library - versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so - we have this short circuit evaluation to find the minimum supported TLS - version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion - because only the former will work before the user's CTX callback is called. +#if LIBWOLFSSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ + /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is + * whatever minimum version of TLS was built in and at least TLS 1.0. For + * later library versions that could change (eg TLS 1.0 built in but + * defaults to TLS 1.1) so we have this short circuit evaluation to find + * the minimum supported TLS version. */ - if((wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1) != 1) && - (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_1) != 1) && - (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_2) != 1) + if((wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1) != 1) && + (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_1) != 1) && + (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_2) != 1) #ifdef WOLFSSL_TLS13 - && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_3) != 1) + && (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_3) != 1) #endif ) { failf(data, "SSL: couldn't set the minimum protocol version"); @@ -261,7 +338,7 @@ cyassl_connect_step1(struct connectdata *conn, ciphers = SSL_CONN_CONFIG(cipher_list); if(ciphers) { - if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) { + if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } @@ -271,7 +348,7 @@ cyassl_connect_step1(struct connectdata *conn, #ifndef NO_FILESYSTEM /* load trusted cacert */ if(SSL_CONN_CONFIG(CAfile)) { - if(1 != SSL_CTX_load_verify_locations(BACKEND->ctx, + if(1 != SSL_CTX_load_verify_locations(backend->ctx, SSL_CONN_CONFIG(CAfile), SSL_CONN_CONFIG(CApath))) { if(SSL_CONN_CONFIG(verifypeer)) { @@ -308,7 +385,7 @@ cyassl_connect_step1(struct connectdata *conn, if(SSL_SET_OPTION(cert) && SSL_SET_OPTION(key)) { int file_type = do_file_type(SSL_SET_OPTION(cert_type)); - if(SSL_CTX_use_certificate_file(BACKEND->ctx, SSL_SET_OPTION(cert), + if(SSL_CTX_use_certificate_file(backend->ctx, SSL_SET_OPTION(cert), file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); @@ -316,7 +393,7 @@ cyassl_connect_step1(struct connectdata *conn, } file_type = do_file_type(SSL_SET_OPTION(key_type)); - if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key), + if(SSL_CTX_use_PrivateKey_file(backend->ctx, SSL_SET_OPTION(key), file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; @@ -328,7 +405,7 @@ cyassl_connect_step1(struct connectdata *conn, * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(BACKEND->ctx, + SSL_CTX_set_verify(backend->ctx, SSL_CONN_CONFIG(verifypeer)?SSL_VERIFY_PEER: SSL_VERIFY_NONE, NULL); @@ -339,15 +416,19 @@ cyassl_connect_step1(struct connectdata *conn, #ifdef ENABLE_IPV6 struct in6_addr addr6; #endif +#ifndef CURL_DISABLE_PROXY const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + const char * const hostname = conn->host.name; +#endif size_t hostname_len = strlen(hostname); if((hostname_len < USHRT_MAX) && (0 == Curl_inet_pton(AF_INET, hostname, &addr4)) && #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, hostname, &addr6)) && #endif - (CyaSSL_CTX_UseSNI(BACKEND->ctx, CYASSL_SNI_HOST_NAME, hostname, + (wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, hostname, (unsigned short)hostname_len) != 1)) { infof(data, "WARNING: failed to configure server name indication (SNI) " "TLS extension\n"); @@ -357,9 +438,8 @@ cyassl_connect_step1(struct connectdata *conn, /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { - CURLcode result = CURLE_OK; - result = (*data->set.ssl.fsslctx)(data, BACKEND->ctx, - data->set.ssl.fsslctxp); + CURLcode result = (*data->set.ssl.fsslctx)(data, backend->ctx, + data->set.ssl.fsslctxp); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; @@ -367,7 +447,7 @@ cyassl_connect_step1(struct connectdata *conn, } #ifdef NO_FILESYSTEM else if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built" + failf(data, "SSL: Certificates can't be loaded because wolfSSL was built" " with \"no filesystem\". Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); @@ -376,10 +456,10 @@ cyassl_connect_step1(struct connectdata *conn, #endif /* Let's make an SSL structure */ - if(BACKEND->handle) - SSL_free(BACKEND->handle); - BACKEND->handle = SSL_new(BACKEND->ctx); - if(!BACKEND->handle) { + if(backend->handle) + SSL_free(backend->handle); + backend->handle = SSL_new(backend->ctx); + if(!backend->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } @@ -402,7 +482,7 @@ cyassl_connect_step1(struct connectdata *conn, strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1); infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1); - if(wolfSSL_UseALPN(BACKEND->handle, protocols, + if(wolfSSL_UseALPN(backend->handle, protocols, (unsigned)strlen(protocols), WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); @@ -411,6 +491,17 @@ cyassl_connect_step1(struct connectdata *conn, } #endif /* HAVE_ALPN */ +#ifdef OPENSSL_EXTRA + if(Curl_tls_keylog_enabled()) { + /* Ensure the Client Random is preserved. */ + wolfSSL_KeepArrays(backend->handle); +#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) + wolfSSL_set_tls13_secret_cb(backend->handle, + wolfssl_tls13_secret_callback, NULL); +#endif + } +#endif /* OPENSSL_EXTRA */ + /* Check if there's a cached ID we can/should use here! */ if(SSL_SET_OPTION(primary.sessionid)) { void *ssl_sessionid = NULL; @@ -418,12 +509,12 @@ cyassl_connect_step1(struct connectdata *conn, Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) { /* we got a session id, use it! */ - if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) { - char error_buffer[CYASSL_MAX_ERROR_SZ]; + if(!SSL_set_session(backend->handle, ssl_sessionid)) { + char error_buffer[WOLFSSL_MAX_ERROR_SZ]; Curl_ssl_sessionid_unlock(conn); failf(data, "SSL: SSL_set_session failed: %s", - ERR_error_string(SSL_get_error(BACKEND->handle, 0), - error_buffer)); + ERR_error_string(SSL_get_error(backend->handle, 0), + error_buffer)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ @@ -433,7 +524,7 @@ cyassl_connect_step1(struct connectdata *conn, } /* pass the raw socket into the SSL layer */ - if(!SSL_set_fd(BACKEND->handle, (int)sockfd)) { + if(!SSL_set_fd(backend->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } @@ -444,34 +535,67 @@ cyassl_connect_step1(struct connectdata *conn, static CURLcode -cyassl_connect_step2(struct connectdata *conn, +wolfssl_connect_step2(struct connectdata *conn, int sockindex) { int ret = -1; struct Curl_easy *data = conn->data; - struct ssl_connect_data* connssl = &conn->ssl[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; +#ifndef CURL_DISABLE_PROXY const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const char * const dispname = SSL_IS_PROXY() ? conn->http_proxy.host.dispname : conn->host.dispname; const char * const pinnedpubkey = SSL_IS_PROXY() ? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : - data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : + data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; +#else + const char * const hostname = conn->host.name; + const char * const dispname = conn->host.dispname; + const char * const pinnedpubkey = + data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; +#endif - conn->recv[sockindex] = cyassl_recv; - conn->send[sockindex] = cyassl_send; + conn->recv[sockindex] = wolfssl_recv; + conn->send[sockindex] = wolfssl_send; /* Enable RFC2818 checks */ if(SSL_CONN_CONFIG(verifyhost)) { - ret = CyaSSL_check_domain_name(BACKEND->handle, hostname); + ret = wolfSSL_check_domain_name(backend->handle, hostname); if(ret == SSL_FAILURE) return CURLE_OUT_OF_MEMORY; } - ret = SSL_connect(BACKEND->handle); + ret = SSL_connect(backend->handle); + +#ifdef OPENSSL_EXTRA + if(Curl_tls_keylog_enabled()) { + /* If key logging is enabled, wait for the handshake to complete and then + * proceed with logging secrets (for TLS 1.2 or older). + * + * During the handshake (ret==-1), wolfSSL_want_read() is true as it waits + * for the server response. At that point the master secret is not yet + * available, so we must not try to read it. + * To log the secret on completion with a handshake failure, detect + * completion via the observation that there is nothing to read or write. + * Note that OpenSSL SSL_want_read() is always true here. If wolfSSL ever + * changes, the worst case is that no key is logged on error. + */ + if(ret == SSL_SUCCESS || + (!wolfSSL_want_read(backend->handle) && + !wolfSSL_want_write(backend->handle))) { + wolfssl_log_tls12_secret(backend->handle); + /* Client Random and master secrets are no longer needed, erase these. + * Ignored while the handshake is still in progress. */ + wolfSSL_FreeArrays(backend->handle); + } + } +#endif /* OPENSSL_EXTRA */ + if(ret != 1) { - char error_buffer[CYASSL_MAX_ERROR_SZ]; - int detail = SSL_get_error(BACKEND->handle, ret); + char error_buffer[WOLFSSL_MAX_ERROR_SZ]; + int detail = SSL_get_error(backend->handle, ret); if(SSL_ERROR_WANT_READ == detail) { connssl->connecting_state = ssl_connect_2_reading; @@ -490,11 +614,12 @@ cyassl_connect_step2(struct connectdata *conn, dispname); return CURLE_PEER_FAILED_VERIFICATION; #else - /* When the CyaSSL_check_domain_name() is used and you desire to continue - * on a DOMAIN_NAME_MISMATCH, i.e. 'conn->ssl_config.verifyhost == 0', - * CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only - * way to do this is currently to switch the CyaSSL_check_domain_name() - * in and out based on the 'conn->ssl_config.verifyhost' value. */ + /* When the wolfssl_check_domain_name() is used and you desire to + * continue on a DOMAIN_NAME_MISMATCH, i.e. 'conn->ssl_config.verifyhost + * == 0', CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA + * error. The only way to do this is currently to switch the + * Wolfssl_check_domain_name() in and out based on the + * 'conn->ssl_config.verifyhost' value. */ if(SSL_CONN_CONFIG(verifyhost)) { failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n", @@ -509,7 +634,7 @@ cyassl_connect_step2(struct connectdata *conn, } #endif } -#if LIBCYASSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */ +#if LIBWOLFSSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */ else if(ASN_NO_SIGNER_E == detail) { if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "\tCA signer not available for verification\n"); @@ -535,17 +660,17 @@ cyassl_connect_step2(struct connectdata *conn, X509 *x509; const char *x509_der; int x509_der_len; - curl_X509certificate x509_parsed; - curl_asn1Element *pubkey; + struct Curl_X509certificate x509_parsed; + struct Curl_asn1Element *pubkey; CURLcode result; - x509 = SSL_get_peer_certificate(BACKEND->handle); + x509 = SSL_get_peer_certificate(backend->handle); if(!x509) { failf(data, "SSL: failed retrieving server certificate"); return CURLE_SSL_PINNEDPUBKEYNOTMATCH; } - x509_der = (const char *)CyaSSL_X509_get_der(x509, &x509_der_len); + x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len); if(!x509_der) { failf(data, "SSL: failed retrieving ASN.1 server certificate"); return CURLE_SSL_PINNEDPUBKEYNOTMATCH; @@ -581,7 +706,7 @@ cyassl_connect_step2(struct connectdata *conn, char *protocol = NULL; unsigned short protocol_len = 0; - rc = wolfSSL_ALPN_GetProtocol(BACKEND->handle, &protocol, &protocol_len); + rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len); if(rc == SSL_SUCCESS) { infof(data, "ALPN, server accepted to use %.*s\n", protocol_len, @@ -613,10 +738,10 @@ cyassl_connect_step2(struct connectdata *conn, #endif /* HAVE_ALPN */ connssl->connecting_state = ssl_connect_3; -#if (LIBCYASSL_VERSION_HEX >= 0x03009010) +#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010) infof(data, "SSL connection using %s / %s\n", - wolfSSL_get_version(BACKEND->handle), - wolfSSL_get_cipher_name(BACKEND->handle)); + wolfSSL_get_version(backend->handle), + wolfSSL_get_cipher_name(backend->handle)); #else infof(data, "SSL connected\n"); #endif @@ -626,12 +751,13 @@ cyassl_connect_step2(struct connectdata *conn, static CURLcode -cyassl_connect_step3(struct connectdata *conn, +wolfssl_connect_step3(struct connectdata *conn, int sockindex) { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -640,7 +766,7 @@ cyassl_connect_step3(struct connectdata *conn, SSL_SESSION *our_ssl_sessionid; void *old_ssl_sessionid = NULL; - our_ssl_sessionid = SSL_get_session(BACKEND->handle); + our_ssl_sessionid = SSL_get_session(backend->handle); Curl_ssl_sessionid_lock(conn); incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL, @@ -671,19 +797,20 @@ cyassl_connect_step3(struct connectdata *conn, } -static ssize_t cyassl_send(struct connectdata *conn, +static ssize_t wolfssl_send(struct connectdata *conn, int sockindex, const void *mem, size_t len, CURLcode *curlcode) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - char error_buffer[CYASSL_MAX_ERROR_SZ]; - int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - int rc = SSL_write(BACKEND->handle, mem, memlen); + struct ssl_backend_data *backend = connssl->backend; + char error_buffer[WOLFSSL_MAX_ERROR_SZ]; + int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; + int rc = SSL_write(backend->handle, mem, memlen); if(rc < 0) { - int err = SSL_get_error(BACKEND->handle, rc); + int err = SSL_get_error(backend->handle, rc); switch(err) { case SSL_ERROR_WANT_READ: @@ -702,34 +829,36 @@ static ssize_t cyassl_send(struct connectdata *conn, return rc; } -static void Curl_cyassl_close(struct connectdata *conn, int sockindex) +static void Curl_wolfssl_close(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; - if(BACKEND->handle) { - (void)SSL_shutdown(BACKEND->handle); - SSL_free(BACKEND->handle); - BACKEND->handle = NULL; + if(backend->handle) { + (void)SSL_shutdown(backend->handle); + SSL_free(backend->handle); + backend->handle = NULL; } - if(BACKEND->ctx) { - SSL_CTX_free(BACKEND->ctx); - BACKEND->ctx = NULL; + if(backend->ctx) { + SSL_CTX_free(backend->ctx); + backend->ctx = NULL; } } -static ssize_t cyassl_recv(struct connectdata *conn, - int num, - char *buf, - size_t buffersize, - CURLcode *curlcode) +static ssize_t wolfssl_recv(struct connectdata *conn, + int num, + char *buf, + size_t buffersize, + CURLcode *curlcode) { struct ssl_connect_data *connssl = &conn->ssl[num]; - char error_buffer[CYASSL_MAX_ERROR_SZ]; - int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - int nread = SSL_read(BACKEND->handle, buf, buffsize); + struct ssl_backend_data *backend = connssl->backend; + char error_buffer[WOLFSSL_MAX_ERROR_SZ]; + int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; + int nread = SSL_read(backend->handle, buf, buffsize); if(nread < 0) { - int err = SSL_get_error(BACKEND->handle, nread); + int err = SSL_get_error(backend->handle, nread); switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ @@ -751,45 +880,48 @@ static ssize_t cyassl_recv(struct connectdata *conn, } -static void Curl_cyassl_session_free(void *ptr) +static void Curl_wolfssl_session_free(void *ptr) { (void)ptr; - /* CyaSSL reuses sessions on own, no free */ + /* wolfSSL reuses sessions on own, no free */ } -static size_t Curl_cyassl_version(char *buffer, size_t size) +static size_t Curl_wolfssl_version(char *buffer, size_t size) { -#if LIBCYASSL_VERSION_HEX >= 0x03006000 +#if LIBWOLFSSL_VERSION_HEX >= 0x03006000 return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version()); #elif defined(WOLFSSL_VERSION) return msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION); -#elif defined(CYASSL_VERSION) - return msnprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION); -#else - return msnprintf(buffer, size, "CyaSSL/%s", "<1.8.8"); #endif } -static int Curl_cyassl_init(void) +static int Curl_wolfssl_init(void) { - return (CyaSSL_Init() == SSL_SUCCESS); +#ifdef OPENSSL_EXTRA + Curl_tls_keylog_open(); +#endif + return (wolfSSL_Init() == SSL_SUCCESS); } -static void Curl_cyassl_cleanup(void) +static void Curl_wolfssl_cleanup(void) { - CyaSSL_Cleanup(); + wolfSSL_Cleanup(); +#ifdef OPENSSL_EXTRA + Curl_tls_keylog_close(); +#endif } -static bool Curl_cyassl_data_pending(const struct connectdata* conn, - int connindex) +static bool Curl_wolfssl_data_pending(const struct connectdata *conn, + int connindex) { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - if(BACKEND->handle) /* SSL is in use */ - return (0 != SSL_pending(BACKEND->handle)) ? TRUE : FALSE; + struct ssl_backend_data *backend = connssl->backend; + if(backend->handle) /* SSL is in use */ + return (0 != SSL_pending(backend->handle)) ? TRUE : FALSE; else return FALSE; } @@ -799,21 +931,22 @@ static bool Curl_cyassl_data_pending(const struct connectdata* conn, * This function is called to shut down the SSL layer but keep the * socket open (CCC - Clear Command Channel) */ -static int Curl_cyassl_shutdown(struct connectdata *conn, int sockindex) +static int Curl_wolfssl_shutdown(struct connectdata *conn, int sockindex) { int retval = 0; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; - if(BACKEND->handle) { - SSL_free(BACKEND->handle); - BACKEND->handle = NULL; + if(backend->handle) { + SSL_free(backend->handle); + backend->handle = NULL; } return retval; } static CURLcode -cyassl_connect_common(struct connectdata *conn, +wolfssl_connect_common(struct connectdata *conn, int sockindex, bool nonblocking, bool *done) @@ -822,7 +955,6 @@ cyassl_connect_common(struct connectdata *conn, struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; - time_t timeout_ms; int what; /* check if the connection has already been established */ @@ -833,7 +965,7 @@ cyassl_connect_common(struct connectdata *conn, if(ssl_connect_1 == connssl->connecting_state) { /* Find out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { /* no need to continue if time already is up */ @@ -841,7 +973,7 @@ cyassl_connect_common(struct connectdata *conn, return CURLE_OPERATION_TIMEDOUT; } - result = cyassl_connect_step1(conn, sockindex); + result = wolfssl_connect_step1(conn, sockindex); if(result) return result; } @@ -851,7 +983,7 @@ cyassl_connect_common(struct connectdata *conn, ssl_connect_2_writing == connssl->connecting_state) { /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { /* no need to continue if time already is up */ @@ -896,7 +1028,7 @@ cyassl_connect_common(struct connectdata *conn, * ensuring that a client using select() or epoll() will always * have a valid fdset to wait on. */ - result = cyassl_connect_step2(conn, sockindex); + result = wolfssl_connect_step2(conn, sockindex); if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || @@ -905,15 +1037,15 @@ cyassl_connect_common(struct connectdata *conn, } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { - result = cyassl_connect_step3(conn, sockindex); + result = wolfssl_connect_step3(conn, sockindex); if(result) return result; } if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; - conn->recv[sockindex] = cyassl_recv; - conn->send[sockindex] = cyassl_send; + conn->recv[sockindex] = wolfssl_recv; + conn->send[sockindex] = wolfssl_send; *done = TRUE; } else @@ -926,19 +1058,19 @@ cyassl_connect_common(struct connectdata *conn, } -static CURLcode Curl_cyassl_connect_nonblocking(struct connectdata *conn, +static CURLcode Curl_wolfssl_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done) { - return cyassl_connect_common(conn, sockindex, TRUE, done); + return wolfssl_connect_common(conn, sockindex, TRUE, done); } -static CURLcode Curl_cyassl_connect(struct connectdata *conn, int sockindex) +static CURLcode Curl_wolfssl_connect(struct connectdata *conn, int sockindex) { CURLcode result; bool done = FALSE; - result = cyassl_connect_common(conn, sockindex, FALSE, &done); + result = wolfssl_connect_common(conn, sockindex, FALSE, &done); if(result) return result; @@ -947,43 +1079,44 @@ static CURLcode Curl_cyassl_connect(struct connectdata *conn, int sockindex) return CURLE_OK; } -static CURLcode Curl_cyassl_random(struct Curl_easy *data, +static CURLcode Curl_wolfssl_random(struct Curl_easy *data, unsigned char *entropy, size_t length) { - RNG rng; + WC_RNG rng; (void)data; - if(InitRng(&rng)) + if(wc_InitRng(&rng)) return CURLE_FAILED_INIT; if(length > UINT_MAX) return CURLE_FAILED_INIT; - if(RNG_GenerateBlock(&rng, entropy, (unsigned)length)) + if(wc_RNG_GenerateBlock(&rng, entropy, (unsigned)length)) return CURLE_FAILED_INIT; - if(FreeRng(&rng)) + if(wc_FreeRng(&rng)) return CURLE_FAILED_INIT; return CURLE_OK; } -static CURLcode Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum /* output */, - size_t unused) +static CURLcode Curl_wolfssl_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum /* output */, + size_t unused) { - Sha256 SHA256pw; + wc_Sha256 SHA256pw; (void)unused; - InitSha256(&SHA256pw); - Sha256Update(&SHA256pw, tmp, (word32)tmplen); - Sha256Final(&SHA256pw, sha256sum); + wc_InitSha256(&SHA256pw); + wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen); + wc_Sha256Final(&SHA256pw, sha256sum); return CURLE_OK; } -static void *Curl_cyassl_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) +static void *Curl_wolfssl_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) { + struct ssl_backend_data *backend = connssl->backend; (void)info; - return BACKEND->handle; + return backend->handle; } -const struct Curl_ssl Curl_ssl_cyassl = { +const struct Curl_ssl Curl_ssl_wolfssl = { { CURLSSLBACKEND_WOLFSSL, "WolfSSL" }, /* info */ #ifdef KEEP_PEER_CERT @@ -993,26 +1126,26 @@ const struct Curl_ssl Curl_ssl_cyassl = { sizeof(struct ssl_backend_data), - Curl_cyassl_init, /* init */ - Curl_cyassl_cleanup, /* cleanup */ - Curl_cyassl_version, /* version */ + Curl_wolfssl_init, /* init */ + Curl_wolfssl_cleanup, /* cleanup */ + Curl_wolfssl_version, /* version */ Curl_none_check_cxn, /* check_cxn */ - Curl_cyassl_shutdown, /* shutdown */ - Curl_cyassl_data_pending, /* data_pending */ - Curl_cyassl_random, /* random */ + Curl_wolfssl_shutdown, /* shutdown */ + Curl_wolfssl_data_pending, /* data_pending */ + Curl_wolfssl_random, /* random */ Curl_none_cert_status_request, /* cert_status_request */ - Curl_cyassl_connect, /* connect */ - Curl_cyassl_connect_nonblocking, /* connect_nonblocking */ - Curl_cyassl_get_internals, /* get_internals */ - Curl_cyassl_close, /* close_one */ + Curl_wolfssl_connect, /* connect */ + Curl_wolfssl_connect_nonblocking, /* connect_nonblocking */ + Curl_wolfssl_get_internals, /* get_internals */ + Curl_wolfssl_close, /* close_one */ Curl_none_close_all, /* close_all */ - Curl_cyassl_session_free, /* session_free */ + Curl_wolfssl_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */ Curl_none_false_start, /* false_start */ Curl_none_md5sum, /* md5sum */ - Curl_cyassl_sha256sum /* sha256sum */ + Curl_wolfssl_sha256sum /* sha256sum */ }; #endif diff --git a/Utilities/cmcurl/lib/vtls/cyassl.h b/Utilities/cmcurl/lib/vtls/wolfssl.h similarity index 79% rename from Utilities/cmcurl/lib/vtls/cyassl.h rename to Utilities/cmcurl/lib/vtls/wolfssl.h index 01e11cc23a7..2b9673c0fe1 100644 --- a/Utilities/cmcurl/lib/vtls/cyassl.h +++ b/Utilities/cmcurl/lib/vtls/wolfssl.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_CYASSL_H -#define HEADER_CURL_CYASSL_H +#ifndef HEADER_CURL_WOLFSSL_H +#define HEADER_CURL_WOLFSSL_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,9 +23,9 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef USE_CYASSL +#ifdef USE_WOLFSSL -extern const struct Curl_ssl Curl_ssl_cyassl; +extern const struct Curl_ssl Curl_ssl_wolfssl; -#endif /* USE_CYASSL */ -#endif /* HEADER_CURL_CYASSL_H */ +#endif /* USE_WOLFSSL */ +#endif /* HEADER_CURL_WOLFSSL_H */ diff --git a/Utilities/cmcurl/lib/warnless.h b/Utilities/cmcurl/lib/warnless.h index ea4c4395dcb..ab78f9448eb 100644 --- a/Utilities/cmcurl/lib/warnless.h +++ b/Utilities/cmcurl/lib/warnless.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -94,19 +94,6 @@ unsigned short curlx_htons(unsigned short usnum); unsigned short curlx_ntohs(unsigned short usnum); -#ifndef BUILDING_WARNLESS_C -# undef FD_ISSET -# define FD_ISSET(a,b) curlx_FD_ISSET((a),(b)) -# undef FD_SET -# define FD_SET(a,b) curlx_FD_SET((a),(b)) -# undef FD_ZERO -# define FD_ZERO(a) curlx_FD_ZERO((a)) -# undef htons -# define htons(a) curlx_htons((a)) -# undef ntohs -# define ntohs(a) curlx_ntohs((a)) -#endif - #endif /* __INTEL_COMPILER && __unix__ */ #endif /* HEADER_CURL_WARNLESS_H */ diff --git a/Utilities/cmcurl/lib/x509asn1.c b/Utilities/cmcurl/lib/x509asn1.c index 0c1256ba899..52747d57df2 100644 --- a/Utilities/cmcurl/lib/x509asn1.c +++ b/Utilities/cmcurl/lib/x509asn1.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,7 +23,7 @@ #include "curl_setup.h" #if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_CYASSL) || defined(USE_SCHANNEL) + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) #include #include "urldata.h" @@ -44,7 +44,7 @@ static const char cnOID[] = "2.5.4.3"; /* Common name. */ static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */ -static const curl_OID OIDtable[] = { +static const struct Curl_OID OIDtable[] = { { "1.2.840.10040.4.1", "dsa" }, { "1.2.840.10040.4.3", "dsa-with-sha1" }, { "1.2.840.10045.2.1", "ecPublicKey" }, @@ -103,16 +103,16 @@ static const curl_OID OIDtable[] = { * Please note there is no pretention here to rewrite a full SSL library. */ -static const char *getASN1Element(curl_asn1Element *elem, +static const char *getASN1Element(struct Curl_asn1Element *elem, const char *beg, const char *end) WARN_UNUSED_RESULT; -static const char *getASN1Element(curl_asn1Element *elem, +static const char *getASN1Element(struct Curl_asn1Element *elem, const char *beg, const char *end) { unsigned char b; unsigned long len; - curl_asn1Element lelem; + struct Curl_asn1Element lelem; /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg' ending at `end'. @@ -176,9 +176,9 @@ static const char *getASN1Element(curl_asn1Element *elem, * Search the null terminated OID or OID identifier in local table. * Return the table entry pointer or NULL if not found. */ -static const curl_OID * searchOID(const char *oid) +static const struct Curl_OID *searchOID(const char *oid) { - const curl_OID *op; + const struct Curl_OID *op; for(op = OIDtable; op->numoid; op++) if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid)) return op; @@ -445,7 +445,7 @@ static const char *OID2str(const char *beg, const char *end, bool symbolic) buf[buflen] = '\0'; if(symbolic) { - const curl_OID *op = searchOID(buf); + const struct Curl_OID *op = searchOID(buf); if(op) { free(buf); buf = strdup(op->textoid); @@ -565,7 +565,7 @@ static const char *UTime2str(const char *beg, const char *end) * Convert an ASN.1 element to a printable string. * Return the dynamically allocated string, or NULL if an error occurs. */ -static const char *ASN1tostr(curl_asn1Element *elem, int type) +static const char *ASN1tostr(struct Curl_asn1Element *elem, int type) { if(elem->constructed) return NULL; /* No conversion of structured elements. */ @@ -609,12 +609,12 @@ static const char *ASN1tostr(curl_asn1Element *elem, int type) * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at * `buf'. Return the total string length, even if larger than `buflen'. */ -static ssize_t encodeDN(char *buf, size_t buflen, curl_asn1Element *dn) +static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn) { - curl_asn1Element rdn; - curl_asn1Element atv; - curl_asn1Element oid; - curl_asn1Element value; + struct Curl_asn1Element rdn; + struct Curl_asn1Element atv; + struct Curl_asn1Element oid; + struct Curl_asn1Element value; size_t l = 0; const char *p1; const char *p2; @@ -683,7 +683,7 @@ static ssize_t encodeDN(char *buf, size_t buflen, curl_asn1Element *dn) * Convert an ASN.1 distinguished name into a printable string. * Return the dynamically allocated string, or NULL if an error occurs. */ -static const char *DNtostr(curl_asn1Element *dn) +static const char *DNtostr(struct Curl_asn1Element *dn) { char *buf = NULL; ssize_t buflen = encodeDN(NULL, 0, dn); @@ -703,11 +703,11 @@ static const char *DNtostr(curl_asn1Element *dn) * Syntax is assumed to have already been checked by the SSL backend. * See RFC 5280. */ -int Curl_parseX509(curl_X509certificate *cert, +int Curl_parseX509(struct Curl_X509certificate *cert, const char *beg, const char *end) { - curl_asn1Element elem; - curl_asn1Element tbsCertificate; + struct Curl_asn1Element elem; + struct Curl_asn1Element tbsCertificate; const char *ccp; static const char defaultVersion = 0; /* v1. */ @@ -835,10 +835,10 @@ static size_t copySubstring(char *to, const char *from) return i; } -static const char *dumpAlgo(curl_asn1Element *param, +static const char *dumpAlgo(struct Curl_asn1Element *param, const char *beg, const char *end) { - curl_asn1Element oid; + struct Curl_asn1Element oid; /* Get algorithm parameters and return algorithm name. */ @@ -855,7 +855,7 @@ static const char *dumpAlgo(curl_asn1Element *param, } static void do_pubkey_field(struct Curl_easy *data, int certnum, - const char *label, curl_asn1Element *elem) + const char *label, struct Curl_asn1Element *elem) { const char *output; @@ -872,11 +872,11 @@ static void do_pubkey_field(struct Curl_easy *data, int certnum, } static void do_pubkey(struct Curl_easy *data, int certnum, - const char *algo, curl_asn1Element *param, - curl_asn1Element *pubkey) + const char *algo, struct Curl_asn1Element *param, + struct Curl_asn1Element *pubkey) { - curl_asn1Element elem; - curl_asn1Element pk; + struct Curl_asn1Element elem; + struct Curl_asn1Element pk; const char *p; /* Generate all information records for the public key. */ @@ -950,9 +950,9 @@ CURLcode Curl_extract_certinfo(struct connectdata *conn, const char *beg, const char *end) { - curl_X509certificate cert; + struct Curl_X509certificate cert; struct Curl_easy *data = conn->data; - curl_asn1Element param; + struct Curl_asn1Element param; const char *ccp; char *cp1; size_t cl1; @@ -1104,14 +1104,14 @@ CURLcode Curl_extract_certinfo(struct connectdata *conn, return CURLE_OK; } -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_CYASSL or USE_SCHANNEL */ +#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL */ #if defined(USE_GSKIT) static const char *checkOID(const char *beg, const char *end, const char *oid) { - curl_asn1Element e; + struct Curl_asn1Element e; const char *ccp; const char *p; bool matched; @@ -1136,22 +1136,21 @@ CURLcode Curl_verifyhost(struct connectdata *conn, const char *beg, const char *end) { struct Curl_easy *data = conn->data; - curl_X509certificate cert; - curl_asn1Element dn; - curl_asn1Element elem; - curl_asn1Element ext; - curl_asn1Element name; + struct Curl_X509certificate cert; + struct Curl_asn1Element dn; + struct Curl_asn1Element elem; + struct Curl_asn1Element ext; + struct Curl_asn1Element name; const char *p; const char *q; char *dnsname; int matched = -1; size_t addrlen = (size_t) -1; ssize_t len; - const char * const hostname = SSL_IS_PROXY()? conn->http_proxy.host.name: - conn->host.name; - const char * const dispname = SSL_IS_PROXY()? - conn->http_proxy.host.dispname: - conn->host.dispname; + const char *const hostname = SSL_IS_PROXY()? + conn->http_proxy.host.name : conn->host.name; + const char *const dispname = SSL_IS_PROXY()? + conn->http_proxy.host.dispname : conn->host.dispname; #ifdef ENABLE_IPV6 struct in6_addr addr; #else diff --git a/Utilities/cmcurl/lib/x509asn1.h b/Utilities/cmcurl/lib/x509asn1.h index ce40297927a..0b7fb881700 100644 --- a/Utilities/cmcurl/lib/x509asn1.h +++ b/Utilities/cmcurl/lib/x509asn1.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,7 +26,7 @@ #include "curl_setup.h" #if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_CYASSL) || defined(USE_SCHANNEL) + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) #include "urldata.h" @@ -79,56 +79,55 @@ */ /* ASN.1 parsed element. */ -typedef struct { - const char * header; /* Pointer to header byte. */ - const char * beg; /* Pointer to element data. */ - const char * end; /* Pointer to 1st byte after element. */ - unsigned char class; /* ASN.1 element class. */ - unsigned char tag; /* ASN.1 element tag. */ - bool constructed; /* Element is constructed. */ -} curl_asn1Element; +struct Curl_asn1Element { + const char *header; /* Pointer to header byte. */ + const char *beg; /* Pointer to element data. */ + const char *end; /* Pointer to 1st byte after element. */ + unsigned char class; /* ASN.1 element class. */ + unsigned char tag; /* ASN.1 element tag. */ + bool constructed; /* Element is constructed. */ +}; /* ASN.1 OID table entry. */ -typedef struct { - const char * numoid; /* Dotted-numeric OID. */ - const char * textoid; /* OID name. */ -} curl_OID; +struct Curl_OID { + const char *numoid; /* Dotted-numeric OID. */ + const char *textoid; /* OID name. */ +}; /* X509 certificate: RFC 5280. */ -typedef struct { - curl_asn1Element certificate; - curl_asn1Element version; - curl_asn1Element serialNumber; - curl_asn1Element signatureAlgorithm; - curl_asn1Element signature; - curl_asn1Element issuer; - curl_asn1Element notBefore; - curl_asn1Element notAfter; - curl_asn1Element subject; - curl_asn1Element subjectPublicKeyInfo; - curl_asn1Element subjectPublicKeyAlgorithm; - curl_asn1Element subjectPublicKey; - curl_asn1Element issuerUniqueID; - curl_asn1Element subjectUniqueID; - curl_asn1Element extensions; -} curl_X509certificate; - +struct Curl_X509certificate { + struct Curl_asn1Element certificate; + struct Curl_asn1Element version; + struct Curl_asn1Element serialNumber; + struct Curl_asn1Element signatureAlgorithm; + struct Curl_asn1Element signature; + struct Curl_asn1Element issuer; + struct Curl_asn1Element notBefore; + struct Curl_asn1Element notAfter; + struct Curl_asn1Element subject; + struct Curl_asn1Element subjectPublicKeyInfo; + struct Curl_asn1Element subjectPublicKeyAlgorithm; + struct Curl_asn1Element subjectPublicKey; + struct Curl_asn1Element issuerUniqueID; + struct Curl_asn1Element subjectUniqueID; + struct Curl_asn1Element extensions; +}; /* * Prototypes. */ -const char *Curl_getASN1Element(curl_asn1Element *elem, - const char *beg, const char *end); -const char *Curl_ASN1tostr(curl_asn1Element *elem, int type); -const char *Curl_DNtostr(curl_asn1Element *dn); -int Curl_parseX509(curl_X509certificate *cert, +const char *Curl_getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end); +const char *Curl_ASN1tostr(struct Curl_asn1Element *elem, int type); +const char *Curl_DNtostr(struct Curl_asn1Element *dn); +int Curl_parseX509(struct Curl_X509certificate *cert, const char *beg, const char *end); CURLcode Curl_extract_certinfo(struct connectdata *conn, int certnum, const char *beg, const char *end); CURLcode Curl_verifyhost(struct connectdata *conn, const char *beg, const char *end); -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_CYASSL or USE_SCHANNEL */ +#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL */ #endif /* HEADER_CURL_X509ASN1_H */ diff --git a/Utilities/cmexpat/ConfigureChecks.cmake b/Utilities/cmexpat/ConfigureChecks.cmake index d85e48c972d..4da252c3325 100644 --- a/Utilities/cmexpat/ConfigureChecks.cmake +++ b/Utilities/cmexpat/ConfigureChecks.cmake @@ -44,14 +44,6 @@ else(WORDS_BIGENDIAN) set(BYTEORDER 1234) endif(WORDS_BIGENDIAN) -if(HAVE_SYS_TYPES_H) - check_symbol_exists("off_t" "sys/types.h" OFF_T) - check_symbol_exists("size_t" "sys/types.h" SIZE_T) -else(HAVE_SYS_TYPES_H) - set(OFF_T "long") - set(SIZE_T "unsigned") -endif(HAVE_SYS_TYPES_H) - check_c_source_compiles(" #include /* for NULL */ #include /* for syscall */ @@ -61,6 +53,3 @@ check_c_source_compiles(" return 0; }" HAVE_SYSCALL_GETRANDOM) - -check_c_compiler_flag("-fno-strict-aliasing" FLAG_NO_STRICT_ALIASING) -check_c_compiler_flag("-fvisibility=hidden" FLAG_VISIBILITY) diff --git a/Utilities/cmexpat/expat_config.h.cmake b/Utilities/cmexpat/expat_config.h.cmake index 795b6078c59..e91861ecf56 100644 --- a/Utilities/cmexpat/expat_config.h.cmake +++ b/Utilities/cmexpat/expat_config.h.cmake @@ -86,9 +86,3 @@ #ifdef _MSC_VER # define __func__ __FUNCTION__ #endif - -/* Define to `long' if does not define. */ -#cmakedefine off_t @OFF_T@ - -/* Define to `unsigned' if does not define. */ -#cmakedefine size_t @SIZE_T@ diff --git a/Utilities/cmexpat/lib/siphash.h b/Utilities/cmexpat/lib/siphash.h index 95f78f83213..a335ec06eb4 100644 --- a/Utilities/cmexpat/lib/siphash.h +++ b/Utilities/cmexpat/lib/siphash.h @@ -97,7 +97,7 @@ #include /* size_t */ -#include +#include #ifndef KWIML_INT_HAVE_UINT64_T # define uint64_t KWIML_INT_uint64_t diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt index 26a9aa9d00c..bfcaf30bb7f 100644 --- a/Utilities/cmlibarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/CMakeLists.txt @@ -1,9 +1,43 @@ +# +IF(0) # CMake handles policy settings in its own build. +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR) +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) #3.12.0 `find_package()`` uses ``_ROOT`` variables. +endif() +ENDIF() +# PROJECT(libarchive C) # SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake") if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${libarchive_BINARY_DIR}/bin) endif() +IF(0) # CMake handles build type selection in its own build. +# +# Set the Build type for make based generators. +# You can choose following types: +# Debug : Debug build +# Release : Release build +# RelWithDebInfo : Release build with Debug Info +# MinSizeRel : Release Min Size build +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE) +ENDIF(NOT CMAKE_BUILD_TYPE) +# Set a value type to properly display CMAKE_BUILD_TYPE on GUI if the +# value type is "UNINITIALIZED". +GET_PROPERTY(cached_type CACHE CMAKE_BUILD_TYPE PROPERTY TYPE) +IF("${cached_type}" STREQUAL "UNINITIALIZED") + SET(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Build Type" FORCE) +ENDIF("${cached_type}" STREQUAL "UNINITIALIZED") +# Check the Build Type. +IF(NOT "${CMAKE_BUILD_TYPE}" + MATCHES "^(Debug|Release|RelWithDebInfo|MinSizeRel)\$") + MESSAGE(FATAL_ERROR + "Unknown keyword for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\n" + "Acceptable keywords: Debug,Release,RelWithDebInfo,MinSizeRel") +ENDIF(NOT "${CMAKE_BUILD_TYPE}" + MATCHES "^(Debug|Release|RelWithDebInfo|MinSizeRel)\$") +ENDIF() # On MacOS, prefer MacPorts libraries to system libraries. # I haven't come up with a compelling argument for this to be conditional. @@ -66,14 +100,122 @@ ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "PathScale") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall") ENDIF() +IF(0) # CMake does not need flags specific to libarchive upstream development. +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + OPTION(ENABLE_WERROR "Treat warnings as errors - default is ON for Debug, OFF otherwise." ON) +else () + OPTION(ENABLE_WERROR "Treat warnings as errors - default is ON for Debug, OFF otherwise." OFF) +endif () + +# Especially for early development, we want to be a little +# aggressive about diagnosing build problems; this can get +# relaxed somewhat in final shipping versions. +IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$") + SET(CMAKE_REQUIRED_FLAGS "-Wall -Wformat -Wformat-security") + ################################################################# + # Set compile flags for all build types. + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat -Wformat-security") + if (ENABLE_WERROR) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + endif () + ################################################################# + # Set compile flags for debug build. + # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wextra") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wunused") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wshadow") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wmissing-prototypes") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wcast-qual") +ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$") +IF (CMAKE_C_COMPILER_ID MATCHES "^Clang$") + SET(CMAKE_REQUIRED_FLAGS "-Wall -Wformat -Wformat-security") + ################################################################# + # Set compile flags for all build types. + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat -Wformat-security") + if (ENABLE_WERROR) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + endif () + ################################################################# + # Set compile flags for debug build. + # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wextra") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wunused") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wshadow") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wmissing-prototypes") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wcast-qual") +ENDIF (CMAKE_C_COMPILER_ID MATCHES "^Clang$") +IF (CMAKE_C_COMPILER_ID MATCHES "^XL$") + SET(CMAKE_C_COMPILER "xlc_r") + SET(CMAKE_REQUIRED_FLAGS "-qflag=e:e -qformat=sec") + ################################################################# + # Set compile flags for all build types. + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -qflag=e:e -qformat=sec") + if (ENABLE_WERROR) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -qhalt=w") + endif () + ################################################################# + # Set compile flags for debug build. + # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qflag=w:w") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qinfo=pro:use") +ENDIF(CMAKE_C_COMPILER_ID MATCHES "^XL$") +IF (MSVC) + if (ENABLE_WERROR) + # /WX option is the same as gcc's -Werror option. + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") + endif () + ################################################################# + # Set compile flags for debug build. + # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" + # Enable level 4 C4062: The enumerate has no associated handler in a switch + # statement and there is no default that can catch it. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4062") + # Enable level 4 C4254: A larger bit field was assigned to a smaller bit + # field. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4254") + # Enable level 4 C4295: An array was initialized but the last character in + # the array is not a null; accessing the array may + # produce unexpected results. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4295") + # Enable level 4 C4296: An unsigned variable was used in a comparison + # operation with zero. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4296") + # Enable level 4 C4389: An operation involved signed and unsigned variables. + # This could result in a loss of data. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4389") + # Enable level 4 C4505: The given function is local and not referenced in + # the body of the module; therefore, the function is + # dead code. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4505") + # Enable level 4 C4514: The optimizer removed an inline function that is not + # called. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4514") + # Enable level 4 C4702: Unreachable code. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4702") + # Enable level 4 C4706: The test value in a conditional expression was the + # result of an assignment. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4706") + # /Oi option enables built-in functions. + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Oi") + ################################################################# + # Set compile flags for release build. + SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Oi") +ENDIF (MSVC) +ENDIF() + # Enable CTest/CDash support include(CTest) -OPTION(ENABLE_NETTLE "Enable use of Nettle" ON) +OPTION(ENABLE_MBEDTLS "Enable use of mbed TLS" OFF) +OPTION(ENABLE_NETTLE "Enable use of Nettle" OFF) OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON) +OPTION(ENABLE_LIBB2 "Enable the use of the system LIBB2 library if found" ON) OPTION(ENABLE_LZ4 "Enable the use of the system LZ4 library if found" ON) OPTION(ENABLE_LZO "Enable the use of the system LZO library if found" OFF) OPTION(ENABLE_LZMA "Enable the use of the system LZMA library if found" ON) +OPTION(ENABLE_ZSTD "Enable the use of the system zstd library if found" ON) OPTION(ENABLE_ZLIB "Enable the use of the system ZLIB library if found" ON) OPTION(ENABLE_BZip2 "Enable the use of the system BZip2 library if found" ON) @@ -84,23 +226,98 @@ OPTION(ENABLE_LibGCC "Enable the use of the system LibGCC library if found" ON) # CNG is used for encrypt/decrypt Zip archives on Windows. OPTION(ENABLE_CNG "Enable the use of CNG(Crypto Next Generation)" ON) +IF(0) # CMake does not build libarchive's command-line tools. +OPTION(ENABLE_TAR "Enable tar building" ON) +OPTION(ENABLE_TAR_SHARED "Enable dynamic build of tar" FALSE) +OPTION(ENABLE_CPIO "Enable cpio building" ON) +OPTION(ENABLE_CPIO_SHARED "Enable dynamic build of cpio" FALSE) +OPTION(ENABLE_CAT "Enable cat building" ON) +OPTION(ENABLE_CAT_SHARED "Enable dynamic build of cat" FALSE) +ENDIF() OPTION(ENABLE_XATTR "Enable extended attribute support" ON) OPTION(ENABLE_ACL "Enable ACL support" ON) OPTION(ENABLE_ICONV "Enable iconv support" ON) +IF(0) # CMake does not build libarchive's tests. +OPTION(ENABLE_TEST "Enable unit and regression tests" ON) +OPTION(ENABLE_COVERAGE "Enable code coverage (GCC only, automatically sets ENABLE_TEST to ON)" FALSE) +OPTION(ENABLE_INSTALL "Enable installing of libraries" ON) + +SET(POSIX_REGEX_LIB "AUTO" CACHE STRING "Choose what library should provide POSIX regular expression support") +SET(ENABLE_SAFESEH "AUTO" CACHE STRING "Enable use of /SAFESEH linker flag (MSVC only)") +SET(WINDOWS_VERSION "WIN7" CACHE STRING "Set Windows version to use (Windows only)") + +IF(ENABLE_COVERAGE) + include(LibarchiveCodeCoverage) +ENDIF(ENABLE_COVERAGE) + +IF(ENABLE_TEST) + ENABLE_TESTING() +ENDIF(ENABLE_TEST) +ENDIF() IF(WIN32) - #ELSEIF(WINDOWS_VERSION STREQUAL "WINXP") SET(NTDDI_VERSION 0x05010000) SET(_WIN32_WINNT 0x0501) SET(WINVER 0x0501) ENDIF(WIN32) -set(HAVE_PTHREAD_H 0) # no threads in CMake +IF(0) # CMake hard-codes its own supported version of Windows. +IF(WIN32) + IF(WINDOWS_VERSION STREQUAL "WIN8") + SET(NTDDI_VERSION 0x06020000) + SET(_WIN32_WINNT 0x0602) + SET(WINVER 0x0602) + ELSEIF(WINDOWS_VERSION STREQUAL "WIN7") + SET(NTDDI_VERSION 0x06010000) + SET(_WIN32_WINNT 0x0601) + SET(WINVER 0x0601) + ELSEIF(WINDOWS_VERSION STREQUAL "WS08") + SET(NTDDI_VERSION 0x06000100) + SET(_WIN32_WINNT 0x0600) + SET(WINVER 0x0600) + ELSEIF(WINDOWS_VERSION STREQUAL "VISTA") + SET(NTDDI_VERSION 0x06000000) + SET(_WIN32_WINNT 0x0600) + SET(WINVER 0x0600) + ELSEIF(WINDOWS_VERSION STREQUAL "WS03") + SET(NTDDI_VERSION 0x05020000) + SET(_WIN32_WINNT 0x0502) + SET(WINVER 0x0502) + ELSEIF(WINDOWS_VERSION STREQUAL "WINXP") + SET(NTDDI_VERSION 0x05010000) + SET(_WIN32_WINNT 0x0501) + SET(WINVER 0x0501) + ELSE(WINDOWS_VERSION STREQUAL "WIN8") + # Default to Windows Server 2003 API if we don't recognize the specifier + SET(NTDDI_VERSION 0x05020000) + SET(_WIN32_WINNT 0x0502) + SET(WINVER 0x0502) + ENDIF(WINDOWS_VERSION STREQUAL "WIN8") +ENDIF(WIN32) + +IF(MSVC) + IF(ENABLE_SAFESEH STREQUAL "YES") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH") + SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH") + SET(ENV{LDFLAGS} "$ENV{LDFLAGS} /SAFESEH") + ELSEIF(ENABLE_SAFESEH STREQUAL "NO") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO") + SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO") + SET(ENV{LDFLAGS} "$ENV{LDFLAGS} /SAFESEH:NO") + ENDIF(ENABLE_SAFESEH STREQUAL "YES") +ENDIF(MSVC) +ENDIF() IF("${CMAKE_C_PLATFORM_ID}" MATCHES "^(HP-UX)$") ADD_DEFINITIONS(-D_XOPEN_SOURCE=500) # Ask wchar.h for mbstate_t ENDIF() +IF(MINGW) + ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO) +ENDIF() + # INCLUDE(CheckCSourceCompiles) INCLUDE(CheckCSourceRuns) @@ -280,7 +497,6 @@ IF(BZIP2_FOUND) ADD_DEFINITIONS(-DUSE_BZIP2_STATIC) ENDIF(USE_BZIP2_DLL) ENDIF(BZIP2_FOUND) -MARK_AS_ADVANCED(CLEAR BZIP2_INCLUDE_DIR) # @@ -289,12 +505,15 @@ MARK_AS_ADVANCED(CLEAR BZIP2_INCLUDE_DIR) IF(ENABLE_LZMA) FIND_PACKAGE(LibLZMA) ELSE() - SET(LIBZMA_FOUND FALSE) # Override cached value + SET(LIBLZMA_FOUND FALSE) # Override cached value ENDIF() IF(LIBLZMA_FOUND) SET(HAVE_LIBLZMA 1) SET(HAVE_LZMA_H 1) + CMAKE_PUSH_CHECK_STATE() + SET(CMAKE_REQUIRED_INCLUDES ${LIBLZMA_INCLUDE_DIR}) + SET(CMAKE_REQUIRED_LIBRARIES ${LIBLZMA_LIBRARIES}) INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS}) LIST(APPEND ADDITIONAL_LIBS ${LIBLZMA_LIBRARIES}) IF(CMAKE_USE_SYSTEM_LIBLZMA) @@ -310,10 +529,10 @@ IF(LIBLZMA_FOUND) ELSE() ADD_DEFINITIONS(-DLZMA_API_STATIC) ENDIF() + CMAKE_POP_CHECK_STATE() ELSE(LIBLZMA_FOUND) # LZMA not found and will not be used. ENDIF(LIBLZMA_FOUND) -IF(0) # CMake does not need LZO2 support in libarchive # # Find LZO2 # @@ -340,10 +559,35 @@ IF(LZO2_FOUND) # TODO: test for static library. # ENDIF(LZO2_FOUND) -MARK_AS_ADVANCED(CLEAR LZO2_INCLUDE_DIR) -MARK_AS_ADVANCED(CLEAR LZO2_LIBRARY) -ENDIF() -IF(0) # CMake does not need LZ4 support in libarchive +# +# Find libb2 +# +IF(ENABLE_LIBB2) + IF (LIBB2_INCLUDE_DIR) + # Already in cache, be silent + SET(LIBB2_FIND_QUIETLY TRUE) + ENDIF (LIBB2_INCLUDE_DIR) + + FIND_PATH(LIBB2_INCLUDE_DIR blake2.h) + FIND_LIBRARY(LIBB2_LIBRARY NAMES b2 libb2) + INCLUDE(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBB2 DEFAULT_MSG LIBB2_LIBRARY LIBB2_INCLUDE_DIR) +ELSE(ENABLE_LIBB2) + SET(LIBB2_FOUND FALSE) # Override cached value +ENDIF(ENABLE_LIBB2) +IF(LIBB2_FOUND) + SET(HAVE_LIBB2 1) + SET(HAVE_BLAKE2_H 1) + SET(ARCHIVE_BLAKE2 FALSE) + LIST(APPEND ADDITIONAL_LIBS ${LIBB2_LIBRARY}) + CMAKE_PUSH_CHECK_STATE() + SET(CMAKE_REQUIRED_LIBRARIES ${LIBB2_LIBRARY}) + SET(CMAKE_REQUIRED_INCLUDES ${LIBB2_INCLUDE_DIR}) + CHECK_FUNCTION_EXISTS(blake2sp_init HAVE_LIBB2) + CMAKE_POP_CHECK_STATE() +ELSE(LIBB2_FOUND) + SET(ARCHIVE_BLAKE2 TRUE) +ENDIF(LIBB2_FOUND) # # Find LZ4 # @@ -373,33 +617,42 @@ IF(LZ4_FOUND) # TODO: test for static library. # ENDIF(LZ4_FOUND) -MARK_AS_ADVANCED(CLEAR LZ4_INCLUDE_DIR) -MARK_AS_ADVANCED(CLEAR LZ4_LIBRARY) -ENDIF() # # Find Zstd # -IF (ZSTD_INCLUDE_DIR) - # Already in cache, be silent - SET(ZSTD_FIND_QUIETLY TRUE) -ENDIF (ZSTD_INCLUDE_DIR) +IF(ENABLE_ZSTD) + IF (ZSTD_INCLUDE_DIR) + # Already in cache, be silent + SET(ZSTD_FIND_QUIETLY TRUE) + ENDIF (ZSTD_INCLUDE_DIR) -FIND_PATH(ZSTD_INCLUDE_DIR zstd.h) -FIND_LIBRARY(ZSTD_LIBRARY NAMES zstd libzstd) -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR) + FIND_PATH(ZSTD_INCLUDE_DIR zstd.h) + FIND_LIBRARY(ZSTD_LIBRARY NAMES zstd libzstd) + INCLUDE(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR) +ELSE(ENABLE_ZSTD) + SET(ZSTD_FOUND FALSE) # Override cached value +ENDIF(ENABLE_ZSTD) IF(ZSTD_FOUND) SET(HAVE_ZSTD_H 1) INCLUDE_DIRECTORIES(${ZSTD_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${ZSTD_LIBRARY}) SET(HAVE_LIBZSTD 1) + IF(0) # CMake expects the zstd library to work. + CMAKE_PUSH_CHECK_STATE() + SET(CMAKE_REQUIRED_LIBRARIES ${ZSTD_LIBRARY}) + SET(CMAKE_REQUIRED_INCLUDES ${ZSTD_INCLUDE_DIR}) + CHECK_FUNCTION_EXISTS(ZSTD_compressStream HAVE_LIBZSTD) # # TODO: test for static library. # + CMAKE_POP_CHECK_STATE() + ENDIF() ENDIF(ZSTD_FOUND) MARK_AS_ADVANCED(CLEAR ZSTD_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR ZSTD_LIBRARY) + # # Check headers # @@ -419,9 +672,11 @@ LA_CHECK_INCLUDE_FILE("sys/types.h" HAVE_SYS_TYPES_H) # Alphabetize the rest unless there's a compelling reason IF(ENABLE_ACL) LA_CHECK_INCLUDE_FILE("acl/libacl.h" HAVE_ACL_LIBACL_H) -ELSE(ENABLE_ACL) + LA_CHECK_INCLUDE_FILE("attr/xattr.h" HAVE_ATTR_XATTR_H) +ELSE() SET(HAVE_ACL_LIBACL_H FALSE) -ENDIF(ENABLE_ACL) + SET(HAVE_ATTR_XATTR_H FALSE) +ENDIF() LA_CHECK_INCLUDE_FILE("ctype.h" HAVE_CTYPE_H) LA_CHECK_INCLUDE_FILE("copyfile.h" HAVE_COPYFILE_H) LA_CHECK_INCLUDE_FILE("direct.h" HAVE_DIRECT_H) @@ -435,7 +690,6 @@ int main(void) { return EXT2_IOC_GETFLAGS; }" HAVE_WORKING_EXT2_IOC_GETFLAGS) LA_CHECK_INCLUDE_FILE("fcntl.h" HAVE_FCNTL_H) LA_CHECK_INCLUDE_FILE("grp.h" HAVE_GRP_H) -LA_CHECK_INCLUDE_FILE("inttypes.h" HAVE_INTTYPES_H) LA_CHECK_INCLUDE_FILE("io.h" HAVE_IO_H) LA_CHECK_INCLUDE_FILE("langinfo.h" HAVE_LANGINFO_H) LA_CHECK_INCLUDE_FILE("limits.h" HAVE_LIMITS_H) @@ -461,7 +715,6 @@ LA_CHECK_INCLUDE_FILE("regex.h" HAVE_REGEX_H) LA_CHECK_INCLUDE_FILE("signal.h" HAVE_SIGNAL_H) LA_CHECK_INCLUDE_FILE("spawn.h" HAVE_SPAWN_H) LA_CHECK_INCLUDE_FILE("stdarg.h" HAVE_STDARG_H) -LA_CHECK_INCLUDE_FILE("stdint.h" HAVE_STDINT_H) LA_CHECK_INCLUDE_FILE("stdlib.h" HAVE_STDLIB_H) LA_CHECK_INCLUDE_FILE("string.h" HAVE_STRING_H) LA_CHECK_INCLUDE_FILE("strings.h" HAVE_STRINGS_H) @@ -518,6 +771,26 @@ CHECK_C_SOURCE_COMPILES( int main() { return 0;}" SAFE_TO_DEFINE_EXTENSIONS) +# +# Find mbed TLS +# +IF(ENABLE_MBEDTLS) + FIND_PACKAGE(MbedTLS) + IF(MBEDTLS_FOUND) + SET(HAVE_LIBMBEDCRYPTO 1) + LIST(APPEND ADDITIONAL_LIBS ${MBEDCRYPTO_LIBRARY}) + INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIRS}) + + LIST(APPEND CMAKE_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIRS}) + LA_CHECK_INCLUDE_FILE("mbedtls/aes.h" HAVE_MBEDTLS_AES_H) + LA_CHECK_INCLUDE_FILE("mbedtls/md.h" HAVE_MBEDTLS_MD_H) + LA_CHECK_INCLUDE_FILE("mbedtls/pkcs5.h" HAVE_MBEDTLS_PKCS5_H) + + ENDIF(MBEDTLS_FOUND) + MARK_AS_ADVANCED(CLEAR MBEDTLS_INCLUDE_DIRS) + MARK_AS_ADVANCED(CLEAR MBEDCRYPTO_LIBRARY) +ENDIF(ENABLE_MBEDTLS) + # # Find Nettle # @@ -579,6 +852,8 @@ MACRO(CHECK_CRYPTO ALGORITHMS IMPLEMENTATION) STRING(TOUPPER "${ALGORITHM}" algorithm) IF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND NOT OPENSSL_FOUND) SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE) + ELSEIF("${IMPLEMENTATION}" MATCHES "^MBEDTLS$" AND NOT MBEDTLS_FOUND) + SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE) ELSEIF("${IMPLEMENTATION}" MATCHES "^NETTLE$" AND NOT NETTLE_FOUND) SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE) ENDIF("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND NOT OPENSSL_FOUND) @@ -594,6 +869,11 @@ MACRO(CHECK_CRYPTO ALGORITHMS IMPLEMENTATION) "${TRY_CRYPTO_REQUIRED_INCLUDES};${OPENSSL_INCLUDE_DIR}") SET(TRY_CRYPTO_REQUIRED_LIBS "-DLINK_LIBRARIES:STRING=${OPENSSL_LIBRARIES}") + ELSEIF("${IMPLEMENTATION}" MATCHES "^MBEDTLS$" AND MBEDTLS_FOUND) + SET(TRY_CRYPTO_REQUIRED_INCLUDES + "${TRY_CRYPTO_REQUIRED_INCLUDES};${MBEDTLS_INCLUDE_DIRS}") + SET(TRY_CRYPTO_REQUIRED_LIBS + "-DLINK_LIBRARIES:STRING=${MBEDCRYPTO_LIBRARY}") ELSEIF("${IMPLEMENTATION}" MATCHES "^NETTLE$" AND NETTLE_FOUND) SET(TRY_CRYPTO_REQUIRED_INCLUDES "${TRY_CRYPTO_REQUIRED_INCLUDES};${NETTLE_INCLUDE_DIR}") @@ -758,18 +1038,19 @@ ENDMACRO(CHECK_CRYPTO_WIN CRYPTO_LIST) MACRO(CHECK_ICONV LIB TRY_ICONV_CONST) IF(NOT HAVE_ICONV) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables - IF (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR - CMAKE_C_COMPILER_ID STREQUAL "Clang") + IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR + CMAKE_C_COMPILER_ID MATCHES "^Clang$") # # During checking iconv proto type, we should use -Werror to avoid the # success of iconv detection with a warnig which success is a miss # detection. So this needs for all build mode(even it's a release mode). # SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") - ENDIF () - IF (CMAKE_C_COMPILER_ID STREQUAL "XL") + ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR + CMAKE_C_COMPILER_ID MATCHES "^Clang$") + IF (CMAKE_C_COMPILER_ID MATCHES "^XL$") SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -qhalt=w -qflag=w:w") - ENDIF () + ENDIF (CMAKE_C_COMPILER_ID MATCHES "^XL$") IF (MSVC) # NOTE: /WX option is the same as gcc's -Werror option. SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} /WX") @@ -889,7 +1170,6 @@ ELSE(ENABLE_ICONV) UNSET(LIBCHARSET_STATIC CACHE) ENDIF(ENABLE_ICONV) -IF(0) # CMake does not need XML support in libarchive # # Find Libxml2 # @@ -936,23 +1216,135 @@ ELSE(LIBXML2_FOUND) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ENDIF(EXPAT_FOUND) ENDIF(LIBXML2_FOUND) -MARK_AS_ADVANCED(CLEAR LIBXML2_INCLUDE_DIR) -MARK_AS_ADVANCED(CLEAR LIBXML2_LIBRARIES) -ENDIF() + +# +# POSIX Regular Expression support +# +IF(POSIX_REGEX_LIB MATCHES "^(AUTO|LIBC|LIBREGEX)$") + # + # If PCREPOSIX is not found or not requested, try using regex + # from libc or libregex + # + FIND_PATH(REGEX_INCLUDE_DIR regex.h) + IF(REGEX_INCLUDE_DIR) + CHECK_FUNCTION_EXISTS_GLIBC(regcomp HAVE_REGCOMP_LIBC) + # + # If libc does not provide regex, find libregex. + # + IF(NOT HAVE_REGCOMP_LIBC) + CMAKE_PUSH_CHECK_STATE() # Save the state of the variables + FIND_LIBRARY(REGEX_LIBRARY regex) + IF(REGEX_LIBRARY) + SET(CMAKE_REQUIRED_LIBRARIES ${REGEX_LIBRARY}) + CHECK_FUNCTION_EXISTS_GLIBC(regcomp HAVE_REGCOMP_LIBREGEX) + IF(HAVE_REGCOMP_LIBREGEX) + LIST(APPEND ADDITIONAL_LIBS ${REGEX_LIBRARY}) + # + # If regex.h is not found, retry looking for regex.h at + # REGEX_INCLUDE_DIR + # + IF(NOT HAVE_REGEX_H) + UNSET(HAVE_REGEX_H CACHE) + INCLUDE_DIRECTORIES(${REGEX_INCLUDE_DIR}) + SET(CMAKE_REQUIRED_INCLUDES ${REGEX_INCLUDE_DIR}) + LA_CHECK_INCLUDE_FILE("regex.h" HAVE_REGEX_H) + ENDIF(NOT HAVE_REGEX_H) + # Test if a macro is needed for the library. + TRY_MACRO_FOR_LIBRARY( + "${REGEX_INCLUDE_DIR}" "${REGEX_LIBRARY}" + COMPILES + "#include \n#include \nint main() {regex_t r;return regcomp(&r, \"\", 0);}" + "USE_REGEX_DLL;USE_REGEX_STATIC") + IF(USE_REGEX_DLL) + ADD_DEFINITIONS(-DUSE_REGEX_DLL) + ELSEIF(USE_REGEX_STATIC) + ADD_DEFINITIONS(-DUSE_REGEX_STATIC) + ENDIF(USE_REGEX_DLL) + ENDIF(HAVE_REGCOMP_LIBREGEX) + ENDIF(REGEX_LIBRARY) + CMAKE_POP_CHECK_STATE() # Restore the state of the variables + ENDIF(NOT HAVE_REGCOMP_LIBC) + ENDIF(REGEX_INCLUDE_DIR) + IF(HAVE_REGCOMP_LIBC OR HAVE_REGCOMP_LIBREGEX) + SET(FOUND_POSIX_REGEX_LIB 1) + ENDIF(HAVE_REGCOMP_LIBC OR HAVE_REGCOMP_LIBREGEX) +ENDIF(POSIX_REGEX_LIB MATCHES "^(AUTO|LIBC|LIBREGEX)$") + +IF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCREPOSIX)$") + # + # If requested, try finding library for PCREPOSIX + # + IF(ENABLE_LibGCC) + FIND_PACKAGE(LibGCC) + ELSE() + SET(LIBGCC_FOUND FALSE) # Override cached value + ENDIF() + IF(ENABLE_PCREPOSIX) + FIND_PACKAGE(PCREPOSIX) + ELSE() + SET(PCREPOSIX_FOUND FALSE) # Override cached value + ENDIF() + IF(PCREPOSIX_FOUND) + INCLUDE_DIRECTORIES(${PCRE_INCLUDE_DIR}) + LIST(APPEND ADDITIONAL_LIBS ${PCREPOSIX_LIBRARIES}) + # Test if a macro is needed for the library. + TRY_MACRO_FOR_LIBRARY( + "${PCRE_INCLUDE_DIR}" "${PCREPOSIX_LIBRARIES}" + COMPILES + "#include \nint main() {regex_t r;return regcomp(&r, \"\", 0);}" + "WITHOUT_PCRE_STATIC;PCRE_STATIC") + IF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) + ADD_DEFINITIONS(-DPCRE_STATIC) + ELSEIF(NOT WITHOUT_PCRE_STATIC AND NOT PCRE_STATIC AND PCRE_FOUND) + # Determine if pcre static libraries are to be used. + LIST(APPEND ADDITIONAL_LIBS ${PCRE_LIBRARIES}) + SET(TMP_LIBRARIES ${PCREPOSIX_LIBRARIES} ${PCRE_LIBRARIES}) + MESSAGE(STATUS "trying again with -lpcre included") + TRY_MACRO_FOR_LIBRARY( + "${PCRE_INCLUDE_DIR}" "${TMP_LIBRARIES}" + COMPILES + "#include \nint main() {regex_t r;return regcomp(&r, \"\", 0);}" + "WITHOUT_PCRE_STATIC;PCRE_STATIC") + IF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) + ADD_DEFINITIONS(-DPCRE_STATIC) + ELSEIF(NOT WITHOUT_PCRE_STATIC AND NOT PCRE_STATIC AND MSVC AND LIBGCC_FOUND) + # When doing a Visual Studio build using pcre static libraries + # built using the mingw toolchain, -lgcc is needed to resolve + # ___chkstk_ms. + MESSAGE(STATUS "Visual Studio build detected, trying again with -lgcc included") + LIST(APPEND ADDITIONAL_LIBS ${LIBGCC_LIBRARIES}) + SET(TMP_LIBRARIES ${PCREPOSIX_LIBRARIES} ${PCRE_LIBRARIES} ${LIBGCC_LIBRARIES}) + TRY_MACRO_FOR_LIBRARY( + "${PCRE_INCLUDE_DIR}" "${TMP_LIBRARIES}" + COMPILES + "#include \nint main() {regex_t r;return regcomp(&r, \"\", 0);}" + "WITHOUT_PCRE_STATIC;PCRE_STATIC") + IF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) + ADD_DEFINITIONS(-DPCRE_STATIC) + ENDIF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) + ENDIF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) + ENDIF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC) + ENDIF(PCREPOSIX_FOUND) + MARK_AS_ADVANCED(CLEAR PCRE_INCLUDE_DIR) + MARK_AS_ADVANCED(CLEAR PCREPOSIX_LIBRARIES) + MARK_AS_ADVANCED(CLEAR PCRE_LIBRARIES) + MARK_AS_ADVANCED(CLEAR LIBGCC_LIBRARIES) +ENDIF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCREPOSIX)$") # # Check functions # CMAKE_PUSH_CHECK_STATE() # Save the state of the variables -IF (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR - CMAKE_C_COMPILER_ID STREQUAL "Clang") +IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR + CMAKE_C_COMPILER_ID MATCHES "^Clang$") # # During checking functions, we should use -fno-builtin to avoid the # failure of function detection which failure is an error "conflicting # types for built-in function" caused by using -Werror option. # SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-builtin") -ENDIF () +ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR + CMAKE_C_COMPILER_ID MATCHES "^Clang$") CHECK_SYMBOL_EXISTS(_CrtSetReportMode "crtdbg.h" HAVE__CrtSetReportMode) CHECK_FUNCTION_EXISTS_GLIBC(arc4random_buf HAVE_ARC4RANDOM_BUF) CHECK_FUNCTION_EXISTS_GLIBC(chflags HAVE_CHFLAGS) @@ -1016,6 +1408,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(strrchr HAVE_STRRCHR) CHECK_FUNCTION_EXISTS_GLIBC(symlink HAVE_SYMLINK) CHECK_FUNCTION_EXISTS_GLIBC(timegm HAVE_TIMEGM) CHECK_FUNCTION_EXISTS_GLIBC(tzset HAVE_TZSET) +CHECK_FUNCTION_EXISTS_GLIBC(unlinkat HAVE_UNLINKAT) CHECK_FUNCTION_EXISTS_GLIBC(unsetenv HAVE_UNSETENV) CHECK_FUNCTION_EXISTS_GLIBC(utime HAVE_UTIME) CHECK_FUNCTION_EXISTS_GLIBC(utimes HAVE_UTIMES) @@ -1029,6 +1422,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(wctomb HAVE_WCTOMB) CHECK_FUNCTION_EXISTS_GLIBC(_ctime64_s HAVE__CTIME64_S) CHECK_FUNCTION_EXISTS_GLIBC(_fseeki64 HAVE__FSEEKI64) CHECK_FUNCTION_EXISTS_GLIBC(_get_timezone HAVE__GET_TIMEZONE) +CHECK_FUNCTION_EXISTS_GLIBC(_gmtime64_s HAVE__GMTIME64_S) CHECK_FUNCTION_EXISTS_GLIBC(_localtime64_s HAVE__LOCALTIME64_S) CHECK_FUNCTION_EXISTS_GLIBC(_mkgmtime64 HAVE__MKGMTIME64) @@ -1092,27 +1486,9 @@ ENDIF(HAVE_STRERROR_R) # # Check defines # -SET(headers "limits.h") -IF(HAVE_STDINT_H) - LIST(APPEND headers "stdint.h") -ENDIF(HAVE_STDINT_H) -IF(HAVE_INTTYPES_H) - LIST(APPEND headers "inttypes.h") -ENDIF(HAVE_INTTYPES_H) CHECK_SYMBOL_EXISTS(EFTYPE "errno.h" HAVE_EFTYPE) CHECK_SYMBOL_EXISTS(EILSEQ "errno.h" HAVE_EILSEQ) CHECK_SYMBOL_EXISTS(D_MD_ORDER "langinfo.h" HAVE_D_MD_ORDER) -CHECK_SYMBOL_EXISTS(INT32_MAX "${headers}" HAVE_DECL_INT32_MAX) -CHECK_SYMBOL_EXISTS(INT32_MIN "${headers}" HAVE_DECL_INT32_MIN) -CHECK_SYMBOL_EXISTS(INT64_MAX "${headers}" HAVE_DECL_INT64_MAX) -CHECK_SYMBOL_EXISTS(INT64_MIN "${headers}" HAVE_DECL_INT64_MIN) -CHECK_SYMBOL_EXISTS(INTMAX_MAX "${headers}" HAVE_DECL_INTMAX_MAX) -CHECK_SYMBOL_EXISTS(INTMAX_MIN "${headers}" HAVE_DECL_INTMAX_MIN) -CHECK_SYMBOL_EXISTS(UINT32_MAX "${headers}" HAVE_DECL_UINT32_MAX) -CHECK_SYMBOL_EXISTS(UINT64_MAX "${headers}" HAVE_DECL_UINT64_MAX) -CHECK_SYMBOL_EXISTS(UINTMAX_MAX "${headers}" HAVE_DECL_UINTMAX_MAX) -CHECK_SYMBOL_EXISTS(SIZE_MAX "${headers}" HAVE_DECL_SIZE_MAX) -CHECK_SYMBOL_EXISTS(SSIZE_MAX "limits.h" HAVE_DECL_SSIZE_MAX) # # Check struct members @@ -1161,33 +1537,6 @@ ENDIF() CHECK_STRUCT_HAS_MEMBER("struct tm" tm_sec "sys/types.h;sys/time.h;time.h" TIME_WITH_SYS_TIME) -# -# Check for integer types -# -# -CHECK_TYPE_SIZE("short" SIZE_OF_SHORT) -CHECK_TYPE_SIZE("int" SIZE_OF_INT) -CHECK_TYPE_SIZE("long" SIZE_OF_LONG) -CHECK_TYPE_SIZE("long long" SIZE_OF_LONG_LONG) - -CHECK_TYPE_SIZE("unsigned short" SIZE_OF_UNSIGNED_SHORT) -CHECK_TYPE_SIZE("unsigned" SIZE_OF_UNSIGNED) -CHECK_TYPE_SIZE("unsigned long" SIZE_OF_UNSIGNED_LONG) -CHECK_TYPE_SIZE("unsigned long long" SIZE_OF_UNSIGNED_LONG_LONG) - -CHECK_TYPE_SIZE("__int64" __INT64) -CHECK_TYPE_SIZE("unsigned __int64" UNSIGNED___INT64) - -CHECK_TYPE_SIZE(int16_t INT16_T) -CHECK_TYPE_SIZE(int32_t INT32_T) -CHECK_TYPE_SIZE(int64_t INT64_T) -CHECK_TYPE_SIZE(intmax_t INTMAX_T) -CHECK_TYPE_SIZE(uint8_t UINT8_T) -CHECK_TYPE_SIZE(uint16_t UINT16_T) -CHECK_TYPE_SIZE(uint32_t UINT32_T) -CHECK_TYPE_SIZE(uint64_t UINT64_T) -CHECK_TYPE_SIZE(uintmax_t UINTMAX_T) - CHECK_TYPE_SIZE(dev_t DEV_T) IF(NOT HAVE_DEV_T) IF(MSVC) @@ -1263,24 +1612,6 @@ IF(NOT HAVE_PID_T) ENDIF(WIN32) ENDIF(NOT HAVE_PID_T) # -CHECK_TYPE_SIZE(intptr_t INTPTR_T) -IF(NOT HAVE_INTPTR_T) - IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) - SET(intptr_t "int64_t") - ELSE() - SET(intptr_t "int32_t") - ENDIF() -ENDIF(NOT HAVE_INTPTR_T) -# -CHECK_TYPE_SIZE(uintptr_t UINTPTR_T) -IF(NOT HAVE_UINTPTR_T) - IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) - SET(uintptr_t "uint64_t") - ELSE() - SET(uintptr_t "uint32_t") - ENDIF() -ENDIF(NOT HAVE_UINTPTR_T) -# CHECK_TYPE_SIZE(wchar_t SIZEOF_WCHAR_T) IF(HAVE_SIZEOF_WCHAR_T) SET(HAVE_WCHAR_T 1) @@ -1297,6 +1628,11 @@ IF(ENABLE_XATTR) CHECK_LIBRARY_EXISTS(attr "setxattr" "" HAVE_LIBATTR) IF(HAVE_LIBATTR) SET(CMAKE_REQUIRED_LIBRARIES "attr") + ELSE() + CHECK_LIBRARY_EXISTS(gnu "setxattr" "" HAVE_LIBATTR_GNU) + IF(HAVE_LIBATTR_GNU) + SET(CMAKE_REQUIRED_LIBRARIES "gnu") + ENDIF() ENDIF(HAVE_LIBATTR) CHECK_SYMBOL_EXISTS(EXTATTR_NAMESPACE_USER "sys/types.h;sys/extattr.h" HAVE_DECL_EXTATTR_NAMESPACE_USER) CHECK_SYMBOL_EXISTS(XATTR_NOFOLLOW "sys/xattr.h" HAVE_DECL_XATTR_NOFOLLOW) @@ -1620,6 +1956,7 @@ CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" LIBC) CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC2) CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC3) CHECK_CRYPTO("MD5;SHA1;SHA256;SHA384;SHA512" LIBSYSTEM) +CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" MBEDTLS) CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" NETTLE) CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" OPENSSL) @@ -1634,6 +1971,11 @@ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) ADD_DEFINITIONS(-DHAVE_CONFIG_H) +IF(0) # CMake does not build libarchive's packages. +# Handle generation of the libarchive.pc file for pkg-config +INCLUDE(CreatePkgConfigFile) +ENDIF() + # # Register installation of PDF documents. # @@ -1659,11 +2001,22 @@ IF(MSVC) ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE) ENDIF(MSVC) +IF(0) # CMake does not build libarchive's tests. +IF(ENABLE_TEST) + ADD_CUSTOM_TARGET(run_all_tests) +ENDIF(ENABLE_TEST) +ENDIF() + # We need CoreServices on Mac OS. IF(APPLE) LIST(APPEND ADDITIONAL_LIBS "-framework CoreServices") ENDIF(APPLE) add_subdirectory(libarchive) +IF(0) # CMake does not build libarchive's command-line tools. +add_subdirectory(cat) +add_subdirectory(tar) +add_subdirectory(cpio) +ENDIF() install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmlibarchive) diff --git a/Utilities/cmlibarchive/COPYING b/Utilities/cmlibarchive/COPYING index 93952b77ae2..14bbefa0d5a 100644 --- a/Utilities/cmlibarchive/COPYING +++ b/Utilities/cmlibarchive/COPYING @@ -23,6 +23,13 @@ the actual statements in the files are controlling. * The following source files are in the public domain: libarchive/archive_getdate.c +* The following source files are triple-licensed with the ability to choose + from CC0 1.0 Universal, OpenSSL or Apache 2.0 licenses: + libarchive/archive_blake2.h + libarchive/archive_blake2_impl.h + libarchive/archive_blake2s_ref.c + libarchive/archive_blake2sp_ref.c + * The build files---including Makefiles, configure scripts, and auxiliary scripts used as part of the compile process---have widely varying licensing terms. Please check individual files before @@ -34,7 +41,7 @@ do use the license below. The varying licensing of the build scripts seems to be an unavoidable mess. -Copyright (c) 2003-2009 +Copyright (c) 2003-2018 All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake b/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake new file mode 100644 index 00000000000..a9163958921 --- /dev/null +++ b/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake @@ -0,0 +1,13 @@ +find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h) + +find_library(MBEDTLS_LIBRARY mbedtls) +find_library(MBEDX509_LIBRARY mbedx509) +find_library(MBEDCRYPTO_LIBRARY mbedcrypto) + +set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}") + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MBEDTLS DEFAULT_MSG + MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) + +mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) diff --git a/Utilities/cmlibarchive/build/cmake/config.h.in b/Utilities/cmlibarchive/build/cmake/config.h.in index 1851d81d0ec..f38601fd7c0 100644 --- a/Utilities/cmlibarchive/build/cmake/config.h.in +++ b/Utilities/cmlibarchive/build/cmake/config.h.in @@ -7,178 +7,6 @@ * Ensure we have C99-style int64_t, etc, all defined. */ -/* First, we need to know if the system has already defined them. */ -#cmakedefine HAVE_INT16_T -#cmakedefine HAVE_INT32_T -#cmakedefine HAVE_INT64_T -#cmakedefine HAVE_INTMAX_T - -#cmakedefine HAVE_UINT8_T -#cmakedefine HAVE_UINT16_T -#cmakedefine HAVE_UINT32_T -#cmakedefine HAVE_UINT64_T -#cmakedefine HAVE_UINTMAX_T - -/* We might have the types we want under other spellings. */ -#cmakedefine HAVE___INT64 -#cmakedefine HAVE_U_INT64_T -#cmakedefine HAVE_UNSIGNED___INT64 - -/* The sizes of various standard integer types. */ -@SIZE_OF_SHORT_CODE@ -@SIZE_OF_INT_CODE@ -@SIZE_OF_LONG_CODE@ -@SIZE_OF_LONG_LONG_CODE@ -@SIZE_OF_UNSIGNED_SHORT_CODE@ -@SIZE_OF_UNSIGNED_CODE@ -@SIZE_OF_UNSIGNED_LONG_CODE@ -@SIZE_OF_UNSIGNED_LONG_LONG_CODE@ - -/* - * If we lack int64_t, define it to the first of __int64, int, long, and long long - * that exists and is the right size. - */ -#if !defined(HAVE_INT64_T) && defined(HAVE___INT64) -typedef __int64 int64_t; -#define HAVE_INT64_T -#endif - -#if !defined(HAVE_INT64_T) && SIZE_OF_INT == 8 -typedef int int64_t; -#define HAVE_INT64_T -#endif - -#if !defined(HAVE_INT64_T) && SIZE_OF_LONG == 8 -typedef long int64_t; -#define HAVE_INT64_T -#endif - -#if !defined(HAVE_INT64_T) && SIZE_OF_LONG_LONG == 8 -typedef long long int64_t; -#define HAVE_INT64_T -#endif - -#if !defined(HAVE_INT64_T) -#error No 64-bit integer type was found. -#endif - -/* - * Similarly for int32_t - */ -#if !defined(HAVE_INT32_T) && SIZE_OF_INT == 4 -typedef int int32_t; -#define HAVE_INT32_T -#endif - -#if !defined(HAVE_INT32_T) && SIZE_OF_LONG == 4 -typedef long int32_t; -#define HAVE_INT32_T -#endif - -#if !defined(HAVE_INT32_T) -#error No 32-bit integer type was found. -#endif - -/* - * Similarly for int16_t - */ -#if !defined(HAVE_INT16_T) && SIZE_OF_INT == 2 -typedef int int16_t; -#define HAVE_INT16_T -#endif - -#if !defined(HAVE_INT16_T) && SIZE_OF_SHORT == 2 -typedef short int16_t; -#define HAVE_INT16_T -#endif - -#if !defined(HAVE_INT16_T) -#error No 16-bit integer type was found. -#endif - -/* - * Similarly for uint64_t - */ -#if !defined(HAVE_UINT64_T) && defined(HAVE_UNSIGNED___INT64) -typedef unsigned __int64 uint64_t; -#define HAVE_UINT64_T -#endif - -#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED == 8 -typedef unsigned uint64_t; -#define HAVE_UINT64_T -#endif - -#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG == 8 -typedef unsigned long uint64_t; -#define HAVE_UINT64_T -#endif - -#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG_LONG == 8 -typedef unsigned long long uint64_t; -#define HAVE_UINT64_T -#endif - -#if !defined(HAVE_UINT64_T) -#error No 64-bit unsigned integer type was found. -#endif - - -/* - * Similarly for uint32_t - */ -#if !defined(HAVE_UINT32_T) && SIZE_OF_UNSIGNED == 4 -typedef unsigned uint32_t; -#define HAVE_UINT32_T -#endif - -#if !defined(HAVE_UINT32_T) && SIZE_OF_UNSIGNED_LONG == 4 -typedef unsigned long uint32_t; -#define HAVE_UINT32_T -#endif - -#if !defined(HAVE_UINT32_T) -#error No 32-bit unsigned integer type was found. -#endif - -/* - * Similarly for uint16_t - */ -#if !defined(HAVE_UINT16_T) && SIZE_OF_UNSIGNED == 2 -typedef unsigned uint16_t; -#define HAVE_UINT16_T -#endif - -#if !defined(HAVE_UINT16_T) && SIZE_OF_UNSIGNED_SHORT == 2 -typedef unsigned short uint16_t; -#define HAVE_UINT16_T -#endif - -#if !defined(HAVE_UINT16_T) -#error No 16-bit unsigned integer type was found. -#endif - -/* - * Similarly for uint8_t - */ -#if !defined(HAVE_UINT8_T) -typedef unsigned char uint8_t; -#define HAVE_UINT8_T -#endif - -#if !defined(HAVE_UINT16_T) -#error No 8-bit unsigned integer type was found. -#endif - -/* Define intmax_t and uintmax_t if they are not already defined. */ -#if !defined(HAVE_INTMAX_T) -typedef int64_t intmax_t; -#endif - -#if !defined(HAVE_UINTMAX_T) -typedef uint64_t uintmax_t; -#endif - /* Define ZLIB_WINAPI if zlib was built on Visual Studio. */ #cmakedefine ZLIB_WINAPI 1 @@ -426,58 +254,14 @@ typedef uint64_t uintmax_t; don't. */ #cmakedefine HAVE_DECL_ACL_USER 1 -/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_INT32_MAX 1 - -/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_INT32_MIN 1 - -/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_INT64_MAX 1 - -/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_INT64_MIN 1 - -/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_INTMAX_MAX 1 - -/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_INTMAX_MIN 1 - /* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't. */ #cmakedefine HAVE_DECL_SETACL 1 -/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_SIZE_MAX 1 - -/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_SSIZE_MAX 1 - /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ #cmakedefine HAVE_DECL_STRERROR_R 1 -/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_UINT32_MAX 1 - -/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_UINT64_MAX 1 - -/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you - don't. */ -#cmakedefine HAVE_DECL_UINTMAX_MAX 1 - /* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if you don't. */ #cmakedefine HAVE_DECL_XATTR_NOFOLLOW 1 @@ -647,9 +431,6 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ICONV_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_INTTYPES_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IO_H 1 @@ -683,6 +464,12 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the `bz2' library (-lbz2). */ #cmakedefine HAVE_LIBBZ2 1 +/* Define to 1 if you have the `b2' library (-lb2). */ +#cmakedefine HAVE_LIBB2 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_BLAKE2_H 1 + /* Define to 1 if you have the `charset' library (-lcharset). */ #cmakedefine HAVE_LIBCHARSET 1 @@ -707,6 +494,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the `lzo2' library (-llzo2). */ #cmakedefine HAVE_LIBLZO2 1 +/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */ +#cmakedefine HAVE_LIBMBEDCRYPTO 1 + /* Define to 1 if you have the `nettle' library (-lnettle). */ #cmakedefine HAVE_LIBNETTLE 1 @@ -946,9 +736,6 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDARG_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_STDINT_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H 1 @@ -1111,6 +898,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 +/* Define to 1 if you have the `unlinkat' function. */ +#cmakedefine HAVE_UNLINKAT 1 + /* Define to 1 if you have the `unsetenv' function. */ #cmakedefine HAVE_UNSETENV 1 @@ -1204,6 +994,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the `_get_timezone' function. */ #cmakedefine HAVE__GET_TIMEZONE 1 +/* Define to 1 if you have the `_gmtime64_s' function. */ +#cmakedefine HAVE__GMTIME64_S 1 + /* Define to 1 if you have the `_localtime64_s' function. */ #cmakedefine HAVE__LOCALTIME64_S 1 @@ -1322,8 +1115,45 @@ typedef uint64_t uintmax_t; /* Define to `int' if doesn't define. */ #cmakedefine uid_t ${uid_t} -/* Define to `int' if does not define. */ -#cmakedefine intptr_t ${intptr_t} +#include -/* Define to `unsigned int' if does not define. */ -#cmakedefine uintptr_t ${uintptr_t} +#ifndef KWIML_INT_HAVE_INT64_T +typedef KWIML_INT_int64_t int64_t; +#endif +#ifndef KWIML_INT_HAVE_INT32_T +typedef KWIML_INT_int32_t int32_t; +#endif +#ifndef KWIML_INT_HAVE_INT16_T +typedef KWIML_INT_int16_t int16_t; +#endif +#ifndef KWIML_INT_HAVE_INT8_T +typedef KWIML_INT_int8_t int8_t; +#endif +#ifndef KWIML_INT_HAVE_INTPTR_T +typedef KWIML_INT_intptr_t intptr_t; +#endif +#ifndef KWIML_INT_HAVE_UINT64_T +typedef KWIML_INT_uint64_t uint64_t; +#endif +#ifndef KWIML_INT_HAVE_UINT32_T +typedef KWIML_INT_uint32_t uint32_t; +#endif +#ifndef KWIML_INT_HAVE_UINT16_T +typedef KWIML_INT_uint16_t uint16_t; +#endif +#ifndef KWIML_INT_HAVE_UINT8_T +typedef KWIML_INT_uint8_t uint8_t; +#endif +#ifndef KWIML_INT_HAVE_UINTPTR_T +typedef KWIML_INT_uintptr_t uintptr_t; +#endif + +/* Define to 1 if you have the header file. */ +#ifdef KWIML_INT_HAVE_STDINT_H +# define HAVE_STDINT_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifdef KWIML_INT_HAVE_INTTYPES_H +# define HAVE_INTTYPES_H 1 +#endif diff --git a/Utilities/cmlibarchive/build/version b/Utilities/cmlibarchive/build/version index 2427eab0f0b..78be3ab7e34 100644 --- a/Utilities/cmlibarchive/build/version +++ b/Utilities/cmlibarchive/build/version @@ -1 +1 @@ -3003003 +3004002 diff --git a/Utilities/cmlibarchive/libarchive/CMakeLists.txt b/Utilities/cmlibarchive/libarchive/CMakeLists.txt index e38d66453b8..891a1406764 100644 --- a/Utilities/cmlibarchive/libarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/libarchive/CMakeLists.txt @@ -51,6 +51,8 @@ SET(libarchive_SOURCES archive_platform_acl.h archive_platform_xattr.h archive_ppmd_private.h + archive_ppmd8.c + archive_ppmd8_private.h archive_ppmd7.c archive_ppmd7_private.h archive_private.h @@ -100,6 +102,7 @@ SET(libarchive_SOURCES archive_read_support_format_lha.c archive_read_support_format_mtree.c archive_read_support_format_rar.c + archive_read_support_format_rar5.c archive_read_support_format_raw.c archive_read_support_format_tar.c archive_read_support_format_warc.c @@ -147,6 +150,7 @@ SET(libarchive_SOURCES archive_write_set_format_iso9660.c archive_write_set_format_mtree.c archive_write_set_format_pax.c + archive_write_set_format_private.h archive_write_set_format_raw.c archive_write_set_format_shar.c archive_write_set_format_ustar.c @@ -167,6 +171,7 @@ SET(libarchive_MANS archive_entry.3 archive_entry_acl.3 archive_entry_linkify.3 + archive_entry_misc.3 archive_entry_paths.3 archive_entry_perms.3 archive_entry_stat.3 @@ -215,6 +220,11 @@ IF(WIN32 AND NOT CYGWIN) LIST(APPEND libarchive_SOURCES filter_fork_windows.c) ENDIF(WIN32 AND NOT CYGWIN) +IF(ARCHIVE_BLAKE2) + LIST(APPEND libarchive_SOURCES archive_blake2sp_ref.c) + LIST(APPEND libarchive_SOURCES archive_blake2s_ref.c) +ENDIF(ARCHIVE_BLAKE2) + IF(ARCHIVE_ACL_DARWIN) LIST(APPEND libarchive_SOURCES archive_disk_acl_darwin.c) ELSEIF(ARCHIVE_ACL_FREEBSD) @@ -228,3 +238,33 @@ ENDIF() # CMake needs just one static "cmlibarchive" library. ADD_LIBRARY(cmlibarchive STATIC ${libarchive_SOURCES} ${include_HEADERS}) TARGET_LINK_LIBRARIES(cmlibarchive ${ADDITIONAL_LIBS}) + +IF(0) # CMake does not build libarchive's full package. +# Libarchive is a shared library +ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS}) +TARGET_INCLUDE_DIRECTORIES(archive PUBLIC .) +TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS}) +SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION}) + +# archive_static is a static library +ADD_LIBRARY(archive_static STATIC ${libarchive_SOURCES} ${include_HEADERS}) +TARGET_LINK_LIBRARIES(archive_static ${ADDITIONAL_LIBS}) +SET_TARGET_PROPERTIES(archive_static PROPERTIES COMPILE_DEFINITIONS + LIBARCHIVE_STATIC) +# On Posix systems, libarchive.so and libarchive.a can co-exist. +IF(NOT WIN32 OR CYGWIN) + SET_TARGET_PROPERTIES(archive_static PROPERTIES OUTPUT_NAME archive) +ENDIF(NOT WIN32 OR CYGWIN) + +IF(ENABLE_INSTALL) + # How to install the libraries + INSTALL(TARGETS archive archive_static + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) + INSTALL_MAN(${libarchive_MANS}) + INSTALL(FILES ${include_HEADERS} DESTINATION include) +ENDIF() + +add_subdirectory(test) +ENDIF() diff --git a/Utilities/cmlibarchive/libarchive/archive.h b/Utilities/cmlibarchive/libarchive/archive.h index f3ebbfe42b5..574e0879ae9 100644 --- a/Utilities/cmlibarchive/libarchive/archive.h +++ b/Utilities/cmlibarchive/libarchive/archive.h @@ -36,7 +36,7 @@ * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3003003 +#define ARCHIVE_VERSION_NUMBER 3004002 #include #include /* for wchar_t */ @@ -52,7 +52,7 @@ */ #if defined(__BORLANDC__) && __BORLANDC__ >= 0x560 # include -#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) +#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) && !defined(__CLANG_INTTYPES_H) # include #endif @@ -152,7 +152,7 @@ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.3.3" +#define ARCHIVE_VERSION_ONLY_STRING "3.4.2" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); @@ -337,6 +337,7 @@ typedef const char *archive_passphrase_callback(struct archive *, #define ARCHIVE_FORMAT_RAR 0xD0000 #define ARCHIVE_FORMAT_7ZIP 0xE0000 #define ARCHIVE_FORMAT_WARC 0xF0000 +#define ARCHIVE_FORMAT_RAR_V5 0x100000 /* * Codes returned by archive_read_format_capabilities(). @@ -446,6 +447,7 @@ __LA_DECL int archive_read_support_format_iso9660(struct archive *); __LA_DECL int archive_read_support_format_lha(struct archive *); __LA_DECL int archive_read_support_format_mtree(struct archive *); __LA_DECL int archive_read_support_format_rar(struct archive *); +__LA_DECL int archive_read_support_format_rar5(struct archive *); __LA_DECL int archive_read_support_format_raw(struct archive *); __LA_DECL int archive_read_support_format_tar(struct archive *); __LA_DECL int archive_read_support_format_warc(struct archive *); @@ -688,6 +690,8 @@ __LA_DECL int archive_read_set_passphrase_callback(struct archive *, #define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000) /* Default: Do not clear no-change flags when unlinking object */ #define ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS (0x20000) +/* Default: Do not extract atomically (using rename) */ +#define ARCHIVE_EXTRACT_SAFE_WRITES (0x40000) __LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, int flags); @@ -1090,6 +1094,8 @@ __LA_DECL int archive_match_excluded(struct archive *, */ __LA_DECL int archive_match_path_excluded(struct archive *, struct archive_entry *); +/* Control recursive inclusion of directory content when directory is included. Default on. */ +__LA_DECL int archive_match_set_inclusion_recursion(struct archive *, int); /* Add exclusion pathname pattern. */ __LA_DECL int archive_match_exclude_pattern(struct archive *, const char *); __LA_DECL int archive_match_exclude_pattern_w(struct archive *, diff --git a/Utilities/cmlibarchive/libarchive/archive_acl.c b/Utilities/cmlibarchive/libarchive/archive_acl.c index 4736531afa1..952e20df4dc 100644 --- a/Utilities/cmlibarchive/libarchive/archive_acl.c +++ b/Utilities/cmlibarchive/libarchive/archive_acl.c @@ -138,14 +138,10 @@ archive_acl_clear(struct archive_acl *acl) free(acl->acl_head); acl->acl_head = ap; } - if (acl->acl_text_w != NULL) { - free(acl->acl_text_w); - acl->acl_text_w = NULL; - } - if (acl->acl_text != NULL) { - free(acl->acl_text); - acl->acl_text = NULL; - } + free(acl->acl_text_w); + acl->acl_text_w = NULL; + free(acl->acl_text); + acl->acl_text = NULL; acl->acl_p = NULL; acl->acl_types = 0; acl->acl_state = 0; /* Not counting. */ @@ -324,14 +320,10 @@ acl_new_entry(struct archive_acl *acl, return (NULL); } - if (acl->acl_text_w != NULL) { - free(acl->acl_text_w); - acl->acl_text_w = NULL; - } - if (acl->acl_text != NULL) { - free(acl->acl_text); - acl->acl_text = NULL; - } + free(acl->acl_text_w); + acl->acl_text_w = NULL; + free(acl->acl_text); + acl->acl_text = NULL; /* * If there's a matching entry already in the list, overwrite it. @@ -753,8 +745,10 @@ archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, append_entry_w(&wp, prefix, ap->type, ap->tag, flags, wname, ap->permset, id); count++; - } else if (r < 0 && errno == ENOMEM) + } else if (r < 0 && errno == ENOMEM) { + free(ws); return (NULL); + } } /* Add terminating character */ @@ -975,8 +969,10 @@ archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, prefix = NULL; r = archive_mstring_get_mbs_l( &ap->name, &name, &len, sc); - if (r != 0) + if (r != 0) { + free(s); return (NULL); + } if (count > 0) *p++ = separator; if (name == NULL || @@ -1581,17 +1577,29 @@ next_field_w(const wchar_t **wp, const wchar_t **start, /* Scan for the separator. */ while (**wp != L'\0' && **wp != L',' && **wp != L':' && - **wp != L'\n') { + **wp != L'\n' && **wp != L'#') { (*wp)++; } *sep = **wp; - /* Trim trailing whitespace to locate end of field. */ - *end = *wp - 1; - while (**end == L' ' || **end == L'\t' || **end == L'\n') { - (*end)--; + /* Locate end of field, trim trailing whitespace if necessary */ + if (*wp == *start) { + *end = *wp; + } else { + *end = *wp - 1; + while (**end == L' ' || **end == L'\t' || **end == L'\n') { + (*end)--; + } + (*end)++; + } + + /* Handle in-field comments */ + if (*sep == L'#') { + while (**wp != L'\0' && **wp != L',' && **wp != L'\n') { + (*wp)++; + } + *sep = **wp; } - (*end)++; /* Adjust scanner location. */ if (**wp != L'\0') @@ -1642,7 +1650,7 @@ archive_acl_from_text_l(struct archive_acl *acl, const char *text, ret = ARCHIVE_OK; types = 0; - while (text != NULL && *text != '\0') { + while (text != NULL && *text != '\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. @@ -1707,6 +1715,11 @@ archive_acl_from_text_l(struct archive_acl *acl, const char *text, st = field[n].start + 1; len = field[n].end - field[n].start; + if (len == 0) { + ret = ARCHIVE_WARN; + continue; + } + switch (*s) { case 'u': if (len == 1 || (len == 4 @@ -2053,17 +2066,30 @@ next_field(const char **p, const char **start, *start = *p; /* Scan for the separator. */ - while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { + while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' && + **p != '#') { (*p)++; } *sep = **p; - /* Trim trailing whitespace to locate end of field. */ - *end = *p - 1; - while (**end == ' ' || **end == '\t' || **end == '\n') { - (*end)--; + /* Locate end of field, trim trailing whitespace if necessary */ + if (*p == *start) { + *end = *p; + } else { + *end = *p - 1; + while (**end == ' ' || **end == '\t' || **end == '\n') { + (*end)--; + } + (*end)++; + } + + /* Handle in-field comments */ + if (*sep == '#') { + while (**p != '\0' && **p != ',' && **p != '\n') { + (*p)++; + } + *sep = **p; } - (*end)++; /* Adjust scanner location. */ if (**p != '\0') diff --git a/Utilities/cmlibarchive/libarchive/archive_acl_private.h b/Utilities/cmlibarchive/libarchive/archive_acl_private.h index ef0b0234cc1..af108162c66 100644 --- a/Utilities/cmlibarchive/libarchive/archive_acl_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_acl_private.h @@ -25,13 +25,13 @@ * $FreeBSD$ */ +#ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED +#define ARCHIVE_ACL_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED -#define ARCHIVE_ACL_PRIVATE_H_INCLUDED - #include "archive_string.h" struct archive_acl_entry { diff --git a/Utilities/cmlibarchive/libarchive/archive_blake2.h b/Utilities/cmlibarchive/libarchive/archive_blake2.h new file mode 100644 index 00000000000..dd6fe6fe5a9 --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_blake2.h @@ -0,0 +1,195 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#ifndef ARCHIVE_BLAKE2_H +#define ARCHIVE_BLAKE2_H + +#include +#include + +#if defined(_MSC_VER) +#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) +#else +#define BLAKE2_PACKED(x) x __attribute__((packed)) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + + enum blake2s_constant + { + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32, + BLAKE2S_KEYBYTES = 32, + BLAKE2S_SALTBYTES = 8, + BLAKE2S_PERSONALBYTES = 8 + }; + + enum blake2b_constant + { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 + }; + + typedef struct blake2s_state__ + { + uint32_t h[8]; + uint32_t t[2]; + uint32_t f[2]; + uint8_t buf[BLAKE2S_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; + } blake2s_state; + + typedef struct blake2b_state__ + { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; + } blake2b_state; + + typedef struct blake2sp_state__ + { + blake2s_state S[8][1]; + blake2s_state R[1]; + uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; + size_t buflen; + size_t outlen; + } blake2sp_state; + + typedef struct blake2bp_state__ + { + blake2b_state S[4][1]; + blake2b_state R[1]; + uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + } blake2bp_state; + + BLAKE2_PACKED(struct blake2s_param__ + { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint16_t xof_length; /* 14 */ + uint8_t node_depth; /* 15 */ + uint8_t inner_length; /* 16 */ + /* uint8_t reserved[0]; */ + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ + }); + + typedef struct blake2s_param__ blake2s_param; + + BLAKE2_PACKED(struct blake2b_param__ + { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ + }); + + typedef struct blake2b_param__ blake2b_param; + + typedef struct blake2xs_state__ + { + blake2s_state S[1]; + blake2s_param P[1]; + } blake2xs_state; + + typedef struct blake2xb_state__ + { + blake2b_state S[1]; + blake2b_param P[1]; + } blake2xb_state; + + /* Padded structs result in a compile-time error */ + enum { + BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), + BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) + }; + + /* Streaming API */ + int blake2s_init( blake2s_state *S, size_t outlen ); + int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); + int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); + int blake2s_final( blake2s_state *S, void *out, size_t outlen ); + + int blake2b_init( blake2b_state *S, size_t outlen ); + int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); + int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); + int blake2b_final( blake2b_state *S, void *out, size_t outlen ); + + int blake2sp_init( blake2sp_state *S, size_t outlen ); + int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); + int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); + + int blake2bp_init( blake2bp_state *S, size_t outlen ); + int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); + int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); + + /* Variable output length API */ + int blake2xs_init( blake2xs_state *S, const size_t outlen ); + int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); + int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); + int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); + + int blake2xb_init( blake2xb_state *S, const size_t outlen ); + int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); + int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); + int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); + + /* Simple API */ + int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + /* This is simply an alias for blake2b */ + int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/Utilities/cmlibarchive/libarchive/archive_blake2_impl.h b/Utilities/cmlibarchive/libarchive/archive_blake2_impl.h new file mode 100644 index 00000000000..0f05defea36 --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_blake2_impl.h @@ -0,0 +1,161 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#ifndef ARCHIVE_BLAKE2_IMPL_H +#define ARCHIVE_BLAKE2_IMPL_H + +#include +#include + +#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) + #if defined(_MSC_VER) + #define BLAKE2_INLINE __inline + #elif defined(__GNUC__) + #define BLAKE2_INLINE __inline__ + #else + #define BLAKE2_INLINE + #endif +#else + #define BLAKE2_INLINE inline +#endif + +static BLAKE2_INLINE uint32_t load32( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint32_t )( p[0] ) << 0) | + (( uint32_t )( p[1] ) << 8) | + (( uint32_t )( p[2] ) << 16) | + (( uint32_t )( p[3] ) << 24) ; +#endif +} + +static BLAKE2_INLINE uint64_t load64( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) | + (( uint64_t )( p[6] ) << 48) | + (( uint64_t )( p[7] ) << 56) ; +#endif +} + +static BLAKE2_INLINE uint16_t load16( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint16_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return ( uint16_t )((( uint32_t )( p[0] ) << 0) | + (( uint32_t )( p[1] ) << 8)); +#endif +} + +static BLAKE2_INLINE void store16( void *dst, uint16_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + *p++ = ( uint8_t )w; w >>= 8; + *p++ = ( uint8_t )w; +#endif +} + +static BLAKE2_INLINE void store32( void *dst, uint32_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); +#endif +} + +static BLAKE2_INLINE void store64( void *dst, uint64_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); + p[6] = (uint8_t)(w >> 48); + p[7] = (uint8_t)(w >> 56); +#endif +} + +static BLAKE2_INLINE uint64_t load48( const void *src ) +{ + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) ; +} + +static BLAKE2_INLINE void store48( void *dst, uint64_t w ) +{ + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); +} + +static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 32 - c ) ); +} + +static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 64 - c ) ); +} + +/* prevents compiler optimizing out memset() */ +static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) +{ + static void *(*const volatile memset_v)(void *, int, size_t) = &memset; + memset_v(v, 0, n); +} + +#endif diff --git a/Utilities/cmlibarchive/libarchive/archive_blake2s_ref.c b/Utilities/cmlibarchive/libarchive/archive_blake2s_ref.c new file mode 100644 index 00000000000..d92ffd0fc56 --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_blake2s_ref.c @@ -0,0 +1,367 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "archive_blake2.h" +#include "archive_blake2_impl.h" + +static const uint32_t blake2s_IV[8] = +{ + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +static const uint8_t blake2s_sigma[10][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + +static void blake2s_set_lastnode( blake2s_state *S ) +{ + S->f[1] = (uint32_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2s_is_lastblock( const blake2s_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2s_set_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_set_lastnode( S ); + + S->f[0] = (uint32_t)-1; +} + +static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + +static void blake2s_init0( blake2s_state *S ) +{ + size_t i; + memset( S, 0, sizeof( blake2s_state ) ); + + for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i]; +} + +/* init2 xors IV with input parameter block */ +int blake2s_init_param( blake2s_state *S, const blake2s_param *P ) +{ + const unsigned char *p = ( const unsigned char * )( P ); + size_t i; + + blake2s_init0( S ); + + /* IV XOR ParamBlock */ + for( i = 0; i < 8; ++i ) + S->h[i] ^= load32( &p[i * 4] ); + + S->outlen = P->digest_length; + return 0; +} + + +/* Sequential blake2s initialization */ +int blake2s_init( blake2s_state *S, size_t outlen ) +{ + blake2s_param P[1]; + + /* Move interval verification here? */ + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + /* memset(P->reserved, 0, sizeof(P->reserved) ); */ + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2s_init_param( S, P ); +} + +int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2s_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + /* memset(P->reserved, 0, sizeof(P->reserved) ); */ + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + if( blake2s_init_param( S, P ) < 0 ) return -1; + + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2s_update( S, block, BLAKE2S_BLOCKBYTES ); + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2s_sigma[r][2*i+0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2*i+1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while(0) + +static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] ) +{ + uint32_t m[16]; + uint32_t v[16]; + size_t i; + + for( i = 0; i < 16; ++i ) { + m[i] = load32( in + i * sizeof( m[i] ) ); + } + + for( i = 0; i < 8; ++i ) { + v[i] = S->h[i]; + } + + v[ 8] = blake2s_IV[0]; + v[ 9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + + for( i = 0; i < 8; ++i ) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2s_update( blake2s_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2S_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2S_BLOCKBYTES) { + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress( S, in ); + in += BLAKE2S_BLOCKBYTES; + inlen -= BLAKE2S_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + +int blake2s_final( blake2s_state *S, void *out, size_t outlen ) +{ + uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; + size_t i; + + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2s_is_lastblock( S ) ) + return -1; + + blake2s_increment_counter( S, ( uint32_t )S->buflen ); + blake2s_set_lastblock( S ); + memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ + blake2s_compress( S, S->buf ); + + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store32( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, outlen ); + secure_zero_memory(buffer, sizeof(buffer)); + return 0; +} + +int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + blake2s_state S[1]; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if ( NULL == key && keylen > 0) return -1; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + if( keylen > BLAKE2S_KEYBYTES ) return -1; + + if( keylen > 0 ) + { + if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1; + } + else + { + if( blake2s_init( S, outlen ) < 0 ) return -1; + } + + blake2s_update( S, ( const uint8_t * )in, inlen ); + blake2s_final( S, out, outlen ); + return 0; +} + +#if defined(SUPERCOP) +int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +{ + return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 ); +} +#endif + +#if defined(BLAKE2S_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); + + if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2s_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2s_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2s_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/Utilities/cmlibarchive/libarchive/archive_blake2sp_ref.c b/Utilities/cmlibarchive/libarchive/archive_blake2sp_ref.c new file mode 100644 index 00000000000..aef101084a8 --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_blake2sp_ref.c @@ -0,0 +1,359 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#if defined(_OPENMP) +#include +#endif + +#include "archive_blake2.h" +#include "archive_blake2_impl.h" + +#define PARALLELISM_DEGREE 8 + +/* + blake2sp_init_param defaults to setting the expecting output length + from the digest_length parameter block field. + + In some cases, however, we do not want this, as the output length + of these instances is given by inner_length instead. +*/ +static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P ) +{ + int err = blake2s_init_param(S, P); + S->outlen = P->inner_length; + return err; +} + +static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint32_t offset ) +{ + blake2s_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, offset ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = BLAKE2S_OUTBYTES; + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2sp_init_leaf_param( S, P ); +} + +static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen ) +{ + blake2s_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 1; + P->inner_length = BLAKE2S_OUTBYTES; + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2s_init_param( S, P ); +} + + +int blake2sp_init( blake2sp_state *S, size_t outlen ) +{ + size_t i; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2sp_init_root( S->R, outlen, 0 ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2sp_init_leaf( S->S[i], outlen, 0, (uint32_t)i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + return 0; +} + +int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ) +{ + size_t i; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2sp_init_root( S->R, outlen, keylen ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2sp_init_leaf( S->S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + + +int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + size_t left = S->buflen; + size_t fill = sizeof( S->buf ) - left; + size_t i; + + if( left && inlen >= fill ) + { + memcpy( S->buf + left, in, fill ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); + + in += fill; + inlen -= fill; + left = 0; + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE) +#else + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2S_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { + blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } + } + + in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); + inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + + if( inlen > 0 ) + memcpy( S->buf + left, in, inlen ); + + S->buflen = left + inlen; + return 0; +} + + +int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + size_t i; + + if(out == NULL || outlen < S->outlen) { + return -1; + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + { + if( S->buflen > i * BLAKE2S_BLOCKBYTES ) + { + size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; + + if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; + + blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); + } + + blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES ); + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES ); + + return blake2s_final( S->R, out, S->outlen ); +} + + +int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + blake2s_state S[PARALLELISM_DEGREE][1]; + blake2s_state FS[1]; + size_t i; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if ( NULL == key && keylen > 0) return -1; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + if( keylen > BLAKE2S_KEYBYTES ) return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2sp_init_leaf( S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1; + + S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ + + if( keylen > 0 ) + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE) +#else + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2S_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { + blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } + + if( inlen__ > i * BLAKE2S_BLOCKBYTES ) + { + const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES; + const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES; + blake2s_update( S[i], in__, len ); + } + + blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES ); + } + + if( blake2sp_init_root( FS, outlen, keylen ) < 0 ) + return -1; + + FS->last_node = 1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES ); + + return blake2s_final( FS, out, outlen ); +} + + + +#if defined(BLAKE2SP_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); + + if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2sp_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2sp_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2sp_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/Utilities/cmlibarchive/libarchive/archive_cmdline_private.h b/Utilities/cmlibarchive/libarchive/archive_cmdline_private.h index 4e409e81481..57a19494fd7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cmdline_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_cmdline_private.h @@ -25,15 +25,15 @@ * $FreeBSD$ */ +#ifndef ARCHIVE_CMDLINE_PRIVATE_H +#define ARCHIVE_CMDLINE_PRIVATE_H + #ifndef __LIBARCHIVE_BUILD #ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif #endif -#ifndef ARCHIVE_CMDLINE_PRIVATE_H -#define ARCHIVE_CMDLINE_PRIVATE_H - struct archive_cmdline { char *path; char **argv; diff --git a/Utilities/cmlibarchive/libarchive/archive_crc32.h b/Utilities/cmlibarchive/libarchive/archive_crc32.h index cd633af89b4..4f1aed30593 100644 --- a/Utilities/cmlibarchive/libarchive/archive_crc32.h +++ b/Utilities/cmlibarchive/libarchive/archive_crc32.h @@ -25,6 +25,9 @@ * $FreeBSD: head/lib/libarchive/archive_crc32.h 201102 2009-12-28 03:11:36Z kientzle $ */ +#ifndef ARCHIVE_CRC32_H +#define ARCHIVE_CRC32_H + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif @@ -76,3 +79,5 @@ crc32(unsigned long crc, const void *_p, size_t len) crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); return (crc ^ 0xffffffffUL); } + +#endif diff --git a/Utilities/cmlibarchive/libarchive/archive_cryptor.c b/Utilities/cmlibarchive/libarchive/archive_cryptor.c index 71967c9d46f..8ab2b097967 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cryptor.c +++ b/Utilities/cmlibarchive/libarchive/archive_cryptor.c @@ -85,6 +85,35 @@ pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, return (BCRYPT_SUCCESS(status)) ? 0: -1; } +#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_PKCS5_H) + +static int +pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, + size_t salt_len, unsigned rounds, uint8_t *derived_key, + size_t derived_key_len) +{ + mbedtls_md_context_t ctx; + const mbedtls_md_info_t *info; + int ret; + + mbedtls_md_init(&ctx); + info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); + if (info == NULL) { + mbedtls_md_free(&ctx); + return (-1); + } + ret = mbedtls_md_setup(&ctx, info, 1); + if (ret != 0) { + mbedtls_md_free(&ctx); + return (-1); + } + ret = mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char *)pw, + pw_len, salt, salt_len, rounds, derived_key_len, derived_key); + + mbedtls_md_free(&ctx); + return (ret); +} + #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H) static int @@ -269,6 +298,39 @@ aes_ctr_release(archive_crypto_ctx *ctx) return 0; } +#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H) + +static int +aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) +{ + mbedtls_aes_init(&ctx->ctx); + ctx->key_len = key_len; + memcpy(ctx->key, key, key_len); + memset(ctx->nonce, 0, sizeof(ctx->nonce)); + ctx->encr_pos = AES_BLOCK_SIZE; + return 0; +} + +static int +aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) +{ + if (mbedtls_aes_setkey_enc(&ctx->ctx, ctx->key, + ctx->key_len * 8) != 0) + return (-1); + if (mbedtls_aes_crypt_ecb(&ctx->ctx, MBEDTLS_AES_ENCRYPT, ctx->nonce, + ctx->encr_buf) != 0) + return (-1); + return 0; +} + +static int +aes_ctr_release(archive_crypto_ctx *ctx) +{ + mbedtls_aes_free(&ctx->ctx); + memset(ctx, 0, sizeof(*ctx)); + return 0; +} + #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H) static int @@ -316,7 +378,14 @@ aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) memcpy(ctx->key, key, key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); ctx->encr_pos = AES_BLOCK_SIZE; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) + if (!EVP_CIPHER_CTX_reset(ctx->ctx)) { + EVP_CIPHER_CTX_free(ctx->ctx); + ctx->ctx = NULL; + } +#else EVP_CIPHER_CTX_init(ctx->ctx); +#endif return 0; } diff --git a/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h b/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h index b9759220df6..64a20556a39 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h @@ -23,13 +23,12 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - #ifndef ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED #define ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif /* * On systems that do not support any recognized crypto libraries, * the archive_cryptor.c file will normally define no usable symbols. @@ -83,6 +82,23 @@ typedef struct { unsigned encr_pos; } archive_crypto_ctx; +#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H) +#include +#include +#include + +#define AES_MAX_KEY_SIZE 32 +#define AES_BLOCK_SIZE 16 + +typedef struct { + mbedtls_aes_context ctx; + uint8_t key[AES_MAX_KEY_SIZE]; + unsigned key_len; + uint8_t nonce[AES_BLOCK_SIZE]; + uint8_t encr_buf[AES_BLOCK_SIZE]; + unsigned encr_pos; +} archive_crypto_ctx; + #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H) #if defined(HAVE_NETTLE_PBKDF2_H) #include diff --git a/Utilities/cmlibarchive/libarchive/archive_digest.c b/Utilities/cmlibarchive/libarchive/archive_digest.c index 41539230314..34c58ac9494 100644 --- a/Utilities/cmlibarchive/libarchive/archive_digest.c +++ b/Utilities/cmlibarchive/libarchive/archive_digest.c @@ -178,6 +178,40 @@ __archive_libsystem_md5final(archive_md5_ctx *ctx, void *md) return (ARCHIVE_OK); } +#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) + +static int +__archive_mbedtls_md5init(archive_md5_ctx *ctx) +{ + mbedtls_md5_init(ctx); + if (mbedtls_md5_starts_ret(ctx) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_md5update(archive_md5_ctx *ctx, const void *indata, + size_t insize) +{ + if (mbedtls_md5_update_ret(ctx, indata, insize) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_md5final(archive_md5_ctx *ctx, void *md) +{ + if (mbedtls_md5_finish_ret(ctx, md) == 0) { + mbedtls_md5_free(ctx); + return (ARCHIVE_OK); + } else { + mbedtls_md5_free(ctx); + return (ARCHIVE_FATAL); + } +} + #elif defined(ARCHIVE_CRYPTO_MD5_NETTLE) static int @@ -335,6 +369,40 @@ __archive_libmd_ripemd160final(archive_rmd160_ctx *ctx, void *md) return (ARCHIVE_OK); } +#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) + +static int +__archive_mbedtls_ripemd160init(archive_rmd160_ctx *ctx) +{ + mbedtls_ripemd160_init(ctx); + if (mbedtls_ripemd160_starts_ret(ctx) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, + size_t insize) +{ + if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_ripemd160final(archive_rmd160_ctx *ctx, void *md) +{ + if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) { + mbedtls_ripemd160_free(ctx); + return (ARCHIVE_OK); + } else { + mbedtls_ripemd160_free(ctx); + return (ARCHIVE_FATAL); + } +} + #elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE) static int @@ -491,6 +559,40 @@ __archive_libsystem_sha1final(archive_sha1_ctx *ctx, void *md) return (ARCHIVE_OK); } +#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) + +static int +__archive_mbedtls_sha1init(archive_sha1_ctx *ctx) +{ + mbedtls_sha1_init(ctx); + if (mbedtls_sha1_starts_ret(ctx) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_sha1update(archive_sha1_ctx *ctx, const void *indata, + size_t insize) +{ + if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_sha1final(archive_sha1_ctx *ctx, void *md) +{ + if (mbedtls_sha1_finish_ret(ctx, md) == 0) { + mbedtls_sha1_free(ctx); + return (ARCHIVE_OK); + } else { + mbedtls_sha1_free(ctx); + return (ARCHIVE_FATAL); + } +} + #elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE) static int @@ -720,6 +822,40 @@ __archive_libsystem_sha256final(archive_sha256_ctx *ctx, void *md) return (ARCHIVE_OK); } +#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) + +static int +__archive_mbedtls_sha256init(archive_sha256_ctx *ctx) +{ + mbedtls_sha256_init(ctx); + if (mbedtls_sha256_starts_ret(ctx, 0) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_sha256update(archive_sha256_ctx *ctx, const void *indata, + size_t insize) +{ + if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_sha256final(archive_sha256_ctx *ctx, void *md) +{ + if (mbedtls_sha256_finish_ret(ctx, md) == 0) { + mbedtls_sha256_free(ctx); + return (ARCHIVE_OK); + } else { + mbedtls_sha256_free(ctx); + return (ARCHIVE_FATAL); + } +} + #elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE) static int @@ -921,6 +1057,40 @@ __archive_libsystem_sha384final(archive_sha384_ctx *ctx, void *md) return (ARCHIVE_OK); } +#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) + +static int +__archive_mbedtls_sha384init(archive_sha384_ctx *ctx) +{ + mbedtls_sha512_init(ctx); + if (mbedtls_sha512_starts_ret(ctx, 1) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_sha384update(archive_sha384_ctx *ctx, const void *indata, + size_t insize) +{ + if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_sha384final(archive_sha384_ctx *ctx, void *md) +{ + if (mbedtls_sha512_finish_ret(ctx, md) == 0) { + mbedtls_sha512_free(ctx); + return (ARCHIVE_OK); + } else { + mbedtls_sha512_free(ctx); + return (ARCHIVE_FATAL); + } +} + #elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE) static int @@ -1146,6 +1316,40 @@ __archive_libsystem_sha512final(archive_sha512_ctx *ctx, void *md) return (ARCHIVE_OK); } +#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) + +static int +__archive_mbedtls_sha512init(archive_sha512_ctx *ctx) +{ + mbedtls_sha512_init(ctx); + if (mbedtls_sha512_starts_ret(ctx, 0) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_sha512update(archive_sha512_ctx *ctx, const void *indata, + size_t insize) +{ + if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0) + return (ARCHIVE_OK); + else + return (ARCHIVE_FATAL); +} + +static int +__archive_mbedtls_sha512final(archive_sha512_ctx *ctx, void *md) +{ + if (mbedtls_sha512_finish_ret(ctx, md) == 0) { + mbedtls_sha512_free(ctx); + return (ARCHIVE_OK); + } else { + mbedtls_sha512_free(ctx); + return (ARCHIVE_FATAL); + } +} + #elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE) static int @@ -1276,6 +1480,10 @@ const struct archive_digest __archive_digest = &__archive_libsystem_md5init, &__archive_libsystem_md5update, &__archive_libsystem_md5final, +#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) + &__archive_mbedtls_md5init, + &__archive_mbedtls_md5update, + &__archive_mbedtls_md5final, #elif defined(ARCHIVE_CRYPTO_MD5_NETTLE) &__archive_nettle_md5init, &__archive_nettle_md5update, @@ -1303,6 +1511,10 @@ const struct archive_digest __archive_digest = &__archive_libmd_ripemd160init, &__archive_libmd_ripemd160update, &__archive_libmd_ripemd160final, +#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) + &__archive_mbedtls_ripemd160init, + &__archive_mbedtls_ripemd160update, + &__archive_mbedtls_ripemd160final, #elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE) &__archive_nettle_ripemd160init, &__archive_nettle_ripemd160update, @@ -1330,6 +1542,10 @@ const struct archive_digest __archive_digest = &__archive_libsystem_sha1init, &__archive_libsystem_sha1update, &__archive_libsystem_sha1final, +#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) + &__archive_mbedtls_sha1init, + &__archive_mbedtls_sha1update, + &__archive_mbedtls_sha1final, #elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE) &__archive_nettle_sha1init, &__archive_nettle_sha1update, @@ -1369,6 +1585,10 @@ const struct archive_digest __archive_digest = &__archive_libsystem_sha256init, &__archive_libsystem_sha256update, &__archive_libsystem_sha256final, +#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) + &__archive_mbedtls_sha256init, + &__archive_mbedtls_sha256update, + &__archive_mbedtls_sha256final, #elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE) &__archive_nettle_sha256init, &__archive_nettle_sha256update, @@ -1404,6 +1624,10 @@ const struct archive_digest __archive_digest = &__archive_libsystem_sha384init, &__archive_libsystem_sha384update, &__archive_libsystem_sha384final, +#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) + &__archive_mbedtls_sha384init, + &__archive_mbedtls_sha384update, + &__archive_mbedtls_sha384final, #elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE) &__archive_nettle_sha384init, &__archive_nettle_sha384update, @@ -1443,6 +1667,10 @@ const struct archive_digest __archive_digest = &__archive_libsystem_sha512init, &__archive_libsystem_sha512update, &__archive_libsystem_sha512final +#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) + &__archive_mbedtls_sha512init, + &__archive_mbedtls_sha512update, + &__archive_mbedtls_sha512final #elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE) &__archive_nettle_sha512init, &__archive_nettle_sha512update, diff --git a/Utilities/cmlibarchive/libarchive/archive_digest_private.h b/Utilities/cmlibarchive/libarchive/archive_digest_private.h index b4fd6ca2252..15312ee9a07 100644 --- a/Utilities/cmlibarchive/libarchive/archive_digest_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_digest_private.h @@ -24,13 +24,12 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef ARCHIVE_DIGEST_PRIVATE_H_INCLUDED +#define ARCHIVE_DIGEST_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif - -#ifndef ARCHIVE_CRYPTO_PRIVATE_H_INCLUDED -#define ARCHIVE_CRYPTO_PRIVATE_H_INCLUDED - /* * Crypto support in various Operating Systems: * @@ -112,6 +111,24 @@ #include #endif +/* mbed TLS crypto headers */ +#if defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) +#include +#endif +#if defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) +#include +#endif +#if defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) +#include +#endif +#if defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) +#include +#endif +#if defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\ + defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) +#include +#endif + /* Nettle crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_NETTLE) #include @@ -159,6 +176,8 @@ typedef MD5_CTX archive_md5_ctx; typedef MD5_CTX archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) typedef CC_MD5_CTX archive_md5_ctx; +#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) +typedef mbedtls_md5_context archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_NETTLE) typedef struct md5_ctx archive_md5_ctx; #elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL) @@ -173,6 +192,8 @@ typedef unsigned char archive_md5_ctx; typedef RMD160_CTX archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD) typedef RIPEMD160_CTX archive_rmd160_ctx; +#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) +typedef mbedtls_ripemd160_context archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE) typedef struct ripemd160_ctx archive_rmd160_ctx; #elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) @@ -187,6 +208,8 @@ typedef SHA1_CTX archive_sha1_ctx; typedef SHA1_CTX archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) typedef CC_SHA1_CTX archive_sha1_ctx; +#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) +typedef mbedtls_sha1_context archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE) typedef struct sha1_ctx archive_sha1_ctx; #elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) @@ -207,6 +230,8 @@ typedef SHA2_CTX archive_sha256_ctx; typedef SHA256_CTX archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) typedef CC_SHA256_CTX archive_sha256_ctx; +#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) +typedef mbedtls_sha256_context archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE) typedef struct sha256_ctx archive_sha256_ctx; #elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) @@ -225,6 +250,8 @@ typedef SHA384_CTX archive_sha384_ctx; typedef SHA2_CTX archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) typedef CC_SHA512_CTX archive_sha384_ctx; +#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) +typedef mbedtls_sha512_context archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE) typedef struct sha384_ctx archive_sha384_ctx; #elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) @@ -245,6 +272,8 @@ typedef SHA2_CTX archive_sha512_ctx; typedef SHA512_CTX archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) typedef CC_SHA512_CTX archive_sha512_ctx; +#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) +typedef mbedtls_sha512_context archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE) typedef struct sha512_ctx archive_sha512_ctx; #elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) @@ -259,6 +288,7 @@ typedef unsigned char archive_sha512_ctx; #if defined(ARCHIVE_CRYPTO_MD5_LIBC) ||\ defined(ARCHIVE_CRYPTO_MD5_LIBMD) || \ defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\ + defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_MD5_NETTLE) ||\ defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_MD5_WIN) @@ -272,6 +302,7 @@ typedef unsigned char archive_sha512_ctx; __archive_digest.md5update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_RMD160_LIBC) ||\ + defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_RMD160_NETTLE) ||\ defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) #define ARCHIVE_HAS_RMD160 @@ -286,6 +317,7 @@ typedef unsigned char archive_sha512_ctx; #if defined(ARCHIVE_CRYPTO_SHA1_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) || \ defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\ + defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA1_WIN) @@ -303,6 +335,7 @@ typedef unsigned char archive_sha512_ctx; defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\ + defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA256_WIN) @@ -319,6 +352,7 @@ typedef unsigned char archive_sha512_ctx; defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\ + defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) @@ -336,6 +370,7 @@ typedef unsigned char archive_sha512_ctx; defined(ARCHIVE_CRYPTO_SHA512_LIBC3) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBMD) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) ||\ + defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) ||\ defined(ARCHIVE_CRYPTO_SHA512_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) diff --git a/Utilities/cmlibarchive/libarchive/archive_disk_acl_sunos.c b/Utilities/cmlibarchive/libarchive/archive_disk_acl_sunos.c index bc84fd6782f..b0f5dfad9b1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_disk_acl_sunos.c +++ b/Utilities/cmlibarchive/libarchive/archive_disk_acl_sunos.c @@ -145,10 +145,8 @@ sunacl_get(int cmd, int *aclcnt, int fd, const char *path) cnt = facl(fd, cmd, cnt, aclp); } } else { - if (aclp != NULL) { - free(aclp); - aclp = NULL; - } + free(aclp); + aclp = NULL; break; } } diff --git a/Utilities/cmlibarchive/libarchive/archive_endian.h b/Utilities/cmlibarchive/libarchive/archive_endian.h index 1c48563b137..e6d3f2ce5e7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_endian.h +++ b/Utilities/cmlibarchive/libarchive/archive_endian.h @@ -28,16 +28,15 @@ * Borrowed from FreeBSD's */ -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif +#ifndef ARCHIVE_ENDIAN_H_INCLUDED +#define ARCHIVE_ENDIAN_H_INCLUDED /* Note: This is a purely internal header! */ /* Do not use this outside of libarchive internal code! */ -#ifndef ARCHIVE_ENDIAN_H_INCLUDED -#define ARCHIVE_ENDIAN_H_INCLUDED - +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif /* * Disabling inline keyword for compilers known to choke on it: diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.3 b/Utilities/cmlibarchive/libarchive/archive_entry.3 index f75916c9e41..2f62a4be233 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry.3 @@ -32,7 +32,7 @@ .Nm archive_entry_clear , .Nm archive_entry_clone , .Nm archive_entry_free , -.Nm archive_entry_new , +.Nm archive_entry_new .Nd functions for managing archive entry descriptions .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) @@ -126,7 +126,6 @@ using the current locale. Similarly, if you store a wide string and then store a narrow string for the same data, the previously-set wide string will be discarded in favor of the new data. -.Pp .\" .Sh EXAMPLE .\" .Sh RETURN VALUES .\" .Sh ERRORS @@ -134,8 +133,8 @@ be discarded in favor of the new data. .Xr archive_entry_acl 3 , .Xr archive_entry_paths 3 , .Xr archive_entry_perms 3 , -.Xr archive_entry_time 3 -.Xr libarchive 3 , +.Xr archive_entry_time 3 , +.Xr libarchive 3 .Sh HISTORY The .Nm libarchive diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.c b/Utilities/cmlibarchive/libarchive/archive_entry.c index f722bbe85c7..a15e98c2842 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry.c @@ -168,6 +168,7 @@ archive_entry_clear(struct archive_entry *entry) archive_entry_xattr_clear(entry); archive_entry_sparse_clear(entry); free(entry->stat); + entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; memset(entry, 0, sizeof(*entry)); return entry; } @@ -202,6 +203,9 @@ archive_entry_clone(struct archive_entry *entry) entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); + /* Copy symlink type */ + entry2->ae_symlink_type = entry->ae_symlink_type; + /* Copy encryption status */ entry2->encryption = entry->encryption; @@ -253,6 +257,7 @@ archive_entry_new2(struct archive *a) if (entry == NULL) return (NULL); entry->archive = a; + entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; return (entry); } @@ -675,6 +680,12 @@ archive_entry_symlink(struct archive_entry *entry) return (NULL); } +int +archive_entry_symlink_type(struct archive_entry *entry) +{ + return (entry->ae_symlink_type); +} + const char * archive_entry_symlink_utf8(struct archive_entry *entry) { @@ -1245,6 +1256,12 @@ archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) entry->ae_set &= ~AE_SET_SYMLINK; } +void +archive_entry_set_symlink_type(struct archive_entry *entry, int type) +{ + entry->ae_symlink_type = type; +} + void archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname) { @@ -1560,10 +1577,8 @@ archive_entry_acl_text_compat(int *flags) const wchar_t * archive_entry_acl_text_w(struct archive_entry *entry, int flags) { - if (entry->acl.acl_text_w != NULL) { - free(entry->acl.acl_text_w); - entry->acl.acl_text_w = NULL; - } + free(entry->acl.acl_text_w); + entry->acl.acl_text_w = NULL; if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl, NULL, flags, entry->archive); @@ -1574,10 +1589,8 @@ archive_entry_acl_text_w(struct archive_entry *entry, int flags) const char * archive_entry_acl_text(struct archive_entry *entry, int flags) { - if (entry->acl.acl_text != NULL) { - free(entry->acl.acl_text); - entry->acl.acl_text = NULL; - } + free(entry->acl.acl_text); + entry->acl.acl_text = NULL; if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL, flags, NULL); @@ -1590,10 +1603,8 @@ int _archive_entry_acl_text_l(struct archive_entry *entry, int flags, const char **acl_text, size_t *len, struct archive_string_conv *sc) { - if (entry->acl.acl_text != NULL) { - free(entry->acl.acl_text); - entry->acl.acl_text = NULL; - } + free(entry->acl.acl_text); + entry->acl.acl_text = NULL; if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, @@ -1638,198 +1649,215 @@ _archive_entry_acl_text_l(struct archive_entry *entry, int flags, * SUCH DAMAGE. */ +/* + * Supported file flags on FreeBSD and Mac OS: + * sappnd,sappend SF_APPEND + * arch,archived SF_ARCHIVED + * schg,schange,simmutable SF_IMMUTABLE + * sunlnk,sunlink SF_NOUNLINK (FreeBSD only) + * uappnd,uappend UF_APPEND + * compressed UF_COMPRESSED (Mac OS only) + * hidden,uhidden UF_HIDDEN + * uchg,uchange,uimmutable UF_IMMUTABLE + * nodump UF_NODUMP + * uunlnk,uunlink UF_NOUNLINK (FreeBSD only) + * offline,uoffline UF_OFFLINE (FreeBSD only) + * opaque UF_OPAQUE + * rdonly,urdonly,readonly UF_READONLY (FreeBSD only) + * reparse,ureparse UF_REPARSE (FreeBSD only) + * sparse,usparse UF_SPARSE (FreeBSD only) + * system,usystem UF_SYSTEM (FreeBSD only) + * + * See chflags(2) for more information + * + * Supported file attributes on Linux: + * a append only FS_APPEND_FL sappnd + * A no atime updates FS_NOATIME_FL atime + * c compress FS_COMPR_FL compress + * C no copy on write FS_NOCOW_FL cow + * d no dump FS_NODUMP_FL dump + * D synchronous directory updates FS_DIRSYNC_FL dirsync + * i immutable FS_IMMUTABLE_FL schg + * j data journalling FS_JOURNAL_DATA_FL journal + * P project hierarchy FS_PROJINHERIT_FL projinherit + * s secure deletion FS_SECRM_FL securedeletion + * S synchronous updates FS_SYNC_FL sync + * t no tail-merging FS_NOTAIL_FL tail + * T top of directory hierarchy FS_TOPDIR_FL topdir + * u undeletable FS_UNRM_FL undel + * + * See ioctl_iflags(2) for more information + * + * Equivalent file flags supported on FreeBSD / Mac OS and Linux: + * SF_APPEND FS_APPEND_FL sappnd + * SF_IMMUTABLE FS_IMMUTABLE_FL schg + * UF_NODUMP FS_NODUMP_FL nodump + */ + static const struct flag { const char *name; const wchar_t *wname; unsigned long set; unsigned long clear; -} flags[] = { +} fileflags[] = { /* Preferred (shorter) names per flag first, all prefixed by "no" */ #ifdef SF_APPEND - { "nosappnd", L"nosappnd", SF_APPEND, 0 }, - { "nosappend", L"nosappend", SF_APPEND, 0 }, + { "nosappnd", L"nosappnd", SF_APPEND, 0}, + { "nosappend", L"nosappend", SF_APPEND, 0}, #endif #if defined(FS_APPEND_FL) /* 'a' */ - { "nosappnd", L"nosappnd", FS_APPEND_FL, 0 }, - { "nosappend", L"nosappend", FS_APPEND_FL, 0 }, + { "nosappnd", L"nosappnd", FS_APPEND_FL, 0}, + { "nosappend", L"nosappend", FS_APPEND_FL, 0}, #elif defined(EXT2_APPEND_FL) /* 'a' */ - { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 }, - { "nosappend", L"nosappend", EXT2_APPEND_FL, 0 }, + { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0}, + { "nosappend", L"nosappend", EXT2_APPEND_FL, 0}, #endif #ifdef SF_ARCHIVED - { "noarch", L"noarch", SF_ARCHIVED, 0 }, - { "noarchived", L"noarchived", SF_ARCHIVED, 0 }, + { "noarch", L"noarch", SF_ARCHIVED, 0}, + { "noarchived", L"noarchived", SF_ARCHIVED, 0}, #endif #ifdef SF_IMMUTABLE - { "noschg", L"noschg", SF_IMMUTABLE, 0 }, - { "noschange", L"noschange", SF_IMMUTABLE, 0 }, - { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 }, + { "noschg", L"noschg", SF_IMMUTABLE, 0}, + { "noschange", L"noschange", SF_IMMUTABLE, 0}, + { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0}, #endif #if defined(FS_IMMUTABLE_FL) /* 'i' */ - { "noschg", L"noschg", FS_IMMUTABLE_FL, 0 }, - { "noschange", L"noschange", FS_IMMUTABLE_FL, 0 }, - { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0 }, + { "noschg", L"noschg", FS_IMMUTABLE_FL, 0}, + { "noschange", L"noschange", FS_IMMUTABLE_FL, 0}, + { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0}, #elif defined(EXT2_IMMUTABLE_FL) /* 'i' */ - { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 }, - { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 }, - { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 }, + { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0}, + { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0}, + { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0}, #endif #ifdef SF_NOUNLINK - { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 }, - { "nosunlink", L"nosunlink", SF_NOUNLINK, 0 }, -#endif -#ifdef SF_SNAPSHOT - { "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 }, + { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0}, + { "nosunlink", L"nosunlink", SF_NOUNLINK, 0}, #endif #ifdef UF_APPEND - { "nouappnd", L"nouappnd", UF_APPEND, 0 }, - { "nouappend", L"nouappend", UF_APPEND, 0 }, + { "nouappnd", L"nouappnd", UF_APPEND, 0}, + { "nouappend", L"nouappend", UF_APPEND, 0}, #endif #ifdef UF_IMMUTABLE - { "nouchg", L"nouchg", UF_IMMUTABLE, 0 }, - { "nouchange", L"nouchange", UF_IMMUTABLE, 0 }, - { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 }, + { "nouchg", L"nouchg", UF_IMMUTABLE, 0}, + { "nouchange", L"nouchange", UF_IMMUTABLE, 0}, + { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0}, #endif #ifdef UF_NODUMP { "nodump", L"nodump", 0, UF_NODUMP}, #endif #if defined(FS_NODUMP_FL) /* 'd' */ { "nodump", L"nodump", 0, FS_NODUMP_FL}, -#elif defined(EXT2_NODUMP_FL) /* 'd' */ +#elif defined(EXT2_NODUMP_FL) { "nodump", L"nodump", 0, EXT2_NODUMP_FL}, #endif #ifdef UF_OPAQUE - { "noopaque", L"noopaque", UF_OPAQUE, 0 }, + { "noopaque", L"noopaque", UF_OPAQUE, 0}, #endif #ifdef UF_NOUNLINK - { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 }, - { "nouunlink", L"nouunlink", UF_NOUNLINK, 0 }, + { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0}, + { "nouunlink", L"nouunlink", UF_NOUNLINK, 0}, #endif #ifdef UF_COMPRESSED - { "nocompressed",L"nocompressed", UF_COMPRESSED, 0 }, + /* Mac OS */ + { "nocompressed", L"nocompressed", UF_COMPRESSED, 0}, #endif #ifdef UF_HIDDEN - { "nohidden", L"nohidden", UF_HIDDEN, 0 }, + { "nohidden", L"nohidden", UF_HIDDEN, 0}, + { "nouhidden", L"nouhidden", UF_HIDDEN, 0}, #endif -#if defined(FS_UNRM_FL) - { "nouunlink", L"nouunlink", FS_UNRM_FL, 0}, -#elif defined(EXT2_UNRM_FL) - { "nouunlink", L"nouunlink", EXT2_UNRM_FL, 0}, +#ifdef FILE_ATTRIBUTE_HIDDEN + { "nohidden", L"nohidden", FILE_ATTRIBUTE_HIDDEN, 0}, + { "nouhidden", L"nouhidden", FILE_ATTRIBUTE_HIDDEN, 0}, #endif - -#if defined(FS_BTREE_FL) - { "nobtree", L"nobtree", FS_BTREE_FL, 0 }, -#elif defined(EXT2_BTREE_FL) - { "nobtree", L"nobtree", EXT2_BTREE_FL, 0 }, +#ifdef UF_OFFLINE + { "nooffline", L"nooffline", UF_OFFLINE, 0}, + { "nouoffline", L"nouoffline", UF_OFFLINE, 0}, #endif - -#if defined(FS_ECOMPR_FL) - { "nocomperr", L"nocomperr", FS_ECOMPR_FL, 0 }, -#elif defined(EXT2_ECOMPR_FL) - { "nocomperr", L"nocomperr", EXT2_ECOMPR_FL, 0 }, +#ifdef UF_READONLY + { "nordonly", L"nordonly", UF_READONLY, 0}, + { "nourdonly", L"nourdonly", UF_READONLY, 0}, + { "noreadonly", L"noreadonly", UF_READONLY, 0}, #endif - -#if defined(FS_COMPR_FL) /* 'c' */ - { "nocompress", L"nocompress", FS_COMPR_FL, 0 }, -#elif defined(EXT2_COMPR_FL) /* 'c' */ - { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, +#ifdef FILE_ATTRIBUTE_READONLY + { "nordonly", L"nordonly", FILE_ATTRIBUTE_READONLY, 0}, + { "nourdonly", L"nourdonly", FILE_ATTRIBUTE_READONLY, 0}, + { "noreadonly", L"noreadonly", FILE_ATTRIBUTE_READONLY, 0}, #endif - -#if defined(FS_NOATIME_FL) /* 'A' */ - { "noatime", L"noatime", 0, FS_NOATIME_FL}, -#elif defined(EXT2_NOATIME_FL) /* 'A' */ - { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, +#ifdef UF_SPARSE + { "nosparse", L"nosparse", UF_SPARSE, 0}, + { "nousparse", L"nousparse", UF_SPARSE, 0}, #endif - -#if defined(FS_DIRTY_FL) - { "nocompdirty",L"nocompdirty", FS_DIRTY_FL, 0}, -#elif defined(EXT2_DIRTY_FL) - { "nocompdirty",L"nocompdirty", EXT2_DIRTY_FL, 0}, +#ifdef UF_REPARSE + { "noreparse", L"noreparse", UF_REPARSE, 0}, + { "noureparse", L"noureparse", UF_REPARSE, 0}, #endif - -#if defined(FS_COMPRBLK_FL) -#if defined(FS_NOCOMPR_FL) - { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, FS_NOCOMPR_FL}, -#else - { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, 0}, +#ifdef UF_SYSTEM + { "nosystem", L"nosystem", UF_SYSTEM, 0}, + { "nousystem", L"nousystem", UF_SYSTEM, 0}, #endif -#elif defined(EXT2_COMPRBLK_FL) -#if defined(EXT2_NOCOMPR_FL) - { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL}, -#else - { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, 0}, +#ifdef FILE_ATTRIBUTE_SYSTEM + { "nosystem", L"nosystem", FILE_ATTRIBUTE_SYSTEM, 0}, + { "nousystem", L"nousystem", FILE_ATTRIBUTE_SYSTEM, 0}, #endif +#if defined(FS_UNRM_FL) /* 'u' */ + { "noundel", L"noundel", FS_UNRM_FL, 0}, +#elif defined(EXT2_UNRM_FL) + { "noundel", L"noundel", EXT2_UNRM_FL, 0}, #endif -#if defined(FS_DIRSYNC_FL) - { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0}, -#elif defined(EXT2_DIRSYNC_FL) - { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0}, + +#if defined(FS_COMPR_FL) /* 'c' */ + { "nocompress", L"nocompress", FS_COMPR_FL, 0}, +#elif defined(EXT2_COMPR_FL) + { "nocompress", L"nocompress", EXT2_COMPR_FL, 0}, #endif -#if defined(FS_INDEX_FL) - { "nohashidx", L"nohashidx", FS_INDEX_FL, 0}, -#elif defined(EXT2_INDEX_FL) - { "nohashidx", L"nohashidx", EXT2_INDEX_FL, 0}, + +#if defined(FS_NOATIME_FL) /* 'A' */ + { "noatime", L"noatime", 0, FS_NOATIME_FL}, +#elif defined(EXT2_NOATIME_FL) + { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, #endif -#if defined(FS_IMAGIC_FL) - { "noimagic", L"noimagic", FS_IMAGIC_FL, 0}, -#elif defined(EXT2_IMAGIC_FL) - { "noimagic", L"noimagic", EXT2_IMAGIC_FL, 0}, +#if defined(FS_DIRSYNC_FL) /* 'D' */ + { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0}, +#elif defined(EXT2_DIRSYNC_FL) + { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0}, #endif -#if defined(FS_JOURNAL_DATA_FL) - { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0}, +#if defined(FS_JOURNAL_DATA_FL) /* 'j' */ + { "nojournal-data",L"nojournal-data", FS_JOURNAL_DATA_FL, 0}, + { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0}, #elif defined(EXT3_JOURNAL_DATA_FL) - { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0}, + { "nojournal-data",L"nojournal-data", EXT3_JOURNAL_DATA_FL, 0}, + { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0}, #endif -#if defined(FS_SECRM_FL) - { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0}, +#if defined(FS_SECRM_FL) /* 's' */ + { "nosecdel", L"nosecdel", FS_SECRM_FL, 0}, + { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0}, #elif defined(EXT2_SECRM_FL) - { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0}, + { "nosecdel", L"nosecdel", EXT2_SECRM_FL, 0}, + { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0}, #endif -#if defined(FS_SYNC_FL) - { "nosync", L"nosync", FS_SYNC_FL, 0}, +#if defined(FS_SYNC_FL) /* 'S' */ + { "nosync", L"nosync", FS_SYNC_FL, 0}, #elif defined(EXT2_SYNC_FL) - { "nosync", L"nosync", EXT2_SYNC_FL, 0}, + { "nosync", L"nosync", EXT2_SYNC_FL, 0}, #endif -#if defined(FS_NOTAIL_FL) - { "notail", L"notail", 0, FS_NOTAIL_FL}, +#if defined(FS_NOTAIL_FL) /* 't' */ + { "notail", L"notail", 0, FS_NOTAIL_FL}, #elif defined(EXT2_NOTAIL_FL) - { "notail", L"notail", 0, EXT2_NOTAIL_FL}, + { "notail", L"notail", 0, EXT2_NOTAIL_FL}, #endif -#if defined(FS_TOPDIR_FL) - { "notopdir", L"notopdir", FS_TOPDIR_FL, 0}, +#if defined(FS_TOPDIR_FL) /* 'T' */ + { "notopdir", L"notopdir", FS_TOPDIR_FL, 0}, #elif defined(EXT2_TOPDIR_FL) - { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0}, -#endif -#ifdef FS_ENCRYPT_FL - { "noencrypt", L"noencrypt", FS_ENCRYPT_FL, 0}, -#endif -#ifdef FS_HUGE_FILE_FL - { "nohugefile", L"nohugefile", FS_HUGE_FILE_FL, 0}, -#endif -#ifdef FS_EXTENT_FL - { "noextent", L"noextent", FS_EXTENT_FL, 0}, -#endif -#ifdef FS_EA_INODE_FL - { "noeainode", L"noeainode", FS_EA_INODE_FL, 0}, -#endif -#ifdef FS_EOFBLOCKS_FL - { "noeofblocks",L"noeofblocks", FS_EOFBLOCKS_FL, 0}, -#endif -#ifdef FS_NOCOW_FL - { "nocow", L"nocow", FS_NOCOW_FL, 0}, -#endif -#ifdef FS_INLINE_DATA_FL - { "noinlinedata",L"noinlinedata", FS_INLINE_DATA_FL, 0}, + { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0}, #endif -#ifdef FS_PROJINHERIT_FL - { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0}, +#ifdef FS_NOCOW_FL /* 'C' */ + { "nocow", L"nocow", 0, FS_NOCOW_FL}, #endif -#if defined(FS_RESERVED_FL) - { "noreserved", L"noreserved", FS_RESERVED_FL, 0}, -#elif defined(EXT2_RESERVED_FL) - { "noreserved", L"noreserved", EXT2_RESERVED_FL, 0}, +#ifdef FS_PROJINHERIT_FL /* 'P' */ + { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0}, #endif - { NULL, NULL, 0, 0 } + { NULL, NULL, 0, 0} }; /* @@ -1848,7 +1876,7 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear) bits = bitset | bitclear; length = 0; - for (flag = flags; flag->name != NULL; flag++) + for (flag = fileflags; flag->name != NULL; flag++) if (bits & (flag->set | flag->clear)) { length += strlen(flag->name) + 1; bits &= ~(flag->set | flag->clear); @@ -1861,7 +1889,7 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear) return (NULL); dp = string; - for (flag = flags; flag->name != NULL; flag++) { + for (flag = fileflags; flag->name != NULL; flag++) { if (bitset & flag->set || bitclear & flag->clear) { sp = flag->name + 2; } else if (bitset & flag->clear || bitclear & flag->set) { @@ -1913,7 +1941,7 @@ ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) *end != ' ' && *end != ',') end++; length = end - start; - for (flag = flags; flag->name != NULL; flag++) { + for (flag = fileflags; flag->name != NULL; flag++) { size_t flag_length = strlen(flag->name); if (length == flag_length && memcmp(start, flag->name, length) == 0) { @@ -1981,7 +2009,7 @@ ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) *end != L' ' && *end != L',') end++; length = end - start; - for (flag = flags; flag->wname != NULL; flag++) { + for (flag = fileflags; flag->wname != NULL; flag++) { size_t flag_length = wcslen(flag->wname); if (length == flag_length && wmemcmp(start, flag->wname, length) == 0) { diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.h b/Utilities/cmlibarchive/libarchive/archive_entry.h index a9134f6b68f..0053faa19f4 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry.h @@ -30,7 +30,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3003003 +#define ARCHIVE_VERSION_NUMBER 3004002 /* * Note: archive_entry.h is for use outside of libarchive; the @@ -187,6 +187,13 @@ struct archive_entry; #define AE_IFDIR ((__LA_MODE_T)0040000) #define AE_IFIFO ((__LA_MODE_T)0010000) +/* + * Symlink types + */ +#define AE_SYMLINK_TYPE_UNDEFINED 0 +#define AE_SYMLINK_TYPE_FILE 1 +#define AE_SYMLINK_TYPE_DIRECTORY 2 + /* * Basic object manipulation */ @@ -272,6 +279,7 @@ __LA_DECL int archive_entry_size_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_strmode(struct archive_entry *); __LA_DECL const char *archive_entry_symlink(struct archive_entry *); __LA_DECL const char *archive_entry_symlink_utf8(struct archive_entry *); +__LA_DECL int archive_entry_symlink_type(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); @@ -347,6 +355,7 @@ __LA_DECL void archive_entry_unset_size(struct archive_entry *); __LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_symlink_type(struct archive_entry *, int); __LA_DECL void archive_entry_set_symlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); @@ -512,9 +521,6 @@ __LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type __LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const char ** /* name */); -__LA_DECL int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */, - int * /* type */, int * /* permset */, int * /* tag */, - int * /* qual */, const wchar_t ** /* name */); /* * Construct a text-format ACL. The flags argument is a bitmask that @@ -689,7 +695,6 @@ __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *, struct archive_entry **, struct archive_entry **); __LA_DECL struct archive_entry *archive_entry_partial_links( struct archive_entry_linkresolver *res, unsigned int *links); - #ifdef __cplusplus } #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 b/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 index 534dbfac6ef..50dd642c20c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 @@ -34,7 +34,6 @@ .Nm archive_entry_acl_from_text , .Nm archive_entry_acl_from_text_w , .Nm archive_entry_acl_next , -.Nm archive_entry_acl_next_w , .Nm archive_entry_acl_reset , .Nm archive_entry_acl_to_text , .Nm archive_entry_acl_to_text_w , @@ -89,16 +88,6 @@ Streaming Archive Library (libarchive, -larchive) .Fa "const char **ret_name" .Fc .Ft int -.Fo archive_entry_acl_next_w -.Fa "struct archive_entry *a" -.Fa "int type" -.Fa "int *ret_type" -.Fa "int *ret_permset" -.Fa "int *ret_tag" -.Fa "int *ret_qual" -.Fa "const wchar_t **ret_name" -.Fc -.Ft int .Fn archive_entry_acl_reset "struct archive_entry *a" "int type" .Ft char * .Fo archive_entry_acl_to_text @@ -118,15 +107,16 @@ Streaming Archive Library (libarchive, -larchive) .Sh DESCRIPTION The .Dq Access Control Lists (ACLs) -extend the standard Unix perssion model. +extend the standard Unix permission model. The ACL interface of .Nm libarchive -supports both POSIX.1e and NFSv4 style ACLs. Use of ACLs is restricted by +supports both POSIX.1e and NFSv4 style ACLs. +Use of ACLs is restricted by various levels of ACL support in operating systems, file systems and archive formats. .Ss POSIX.1e Access Control Lists A POSIX.1e ACL consists of a number of independent entries. -Each entry specifies the permission set as bitmask of basic permissions. +Each entry specifies the permission set as a bitmask of basic permissions. Valid permissions in the .Fa permset are: @@ -147,13 +137,13 @@ The user specified by the name field. .It Dv ARCHIVE_ENTRY_ACL_USER_OBJ The owner of the file. .It Dv ARCHIVE_ENTRY_ACL_GROUP -The group specied by the name field. +The group specified by the name field. .It Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ -The group who owns the file. +The group which owns the file. .It Dv ARCHIVE_ENTRY_ACL_MASK The maximum permissions to be obtained via group permissions. .It Dv ARCHIVE_ENTRY_ACL_OTHER -Any principal who is not file owner or a member of the owning group. +Any principal who is not the file owner or a member of the owning group. .El .Pp The principals @@ -164,12 +154,12 @@ and are equivalent to user, group and other in the classic Unix permission model and specify non-extended ACL entries. .Pp -All files with have an access ACL +All files have an access ACL .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS . This specifies the permissions required for access to the file itself. Directories have an additional ACL .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT , -which controls the initial access ACL for newly created directory entries. +which controls the initial access ACL for newly-created directory entries. .Ss NFSv4 Access Control Lists A NFSv4 ACL consists of multiple individual entries called Access Control Entries (ACEs). @@ -197,11 +187,11 @@ The user specified by the name field. .It Dv ARCHIVE_ENTRY_ACL_USER_OBJ The owner of the file. .It Dv ARCHIVE_ENTRY_ACL_GROUP -The group specied by the name field. +The group specified by the name field. .It Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ -The group who owns the file. +The group which owns the file. .It Dv ARCHIVE_ENTRY_ACL_EVERYONE -Any principal who is not file owner or a member of the owning group. +Any principal who is not the file owner or a member of the owning group. .El .Pp Entries with the @@ -216,9 +206,10 @@ integer. .Pp NFSv4 ACE permissions and flags are stored in the same .Fa permset -bitfield. Some permissions share the same constant and permission character but -have different effect on directories than on files. The following ACE -permissions are supported: +bitfield. +Some permissions share the same constant and permission character +but have different effect on directories than on files. +The following ACE permissions are supported: .Bl -tag -offset indent -compact -width ARCHIV .It Dv ARCHIVE_ENTRY_ACL_READ_DATA ( Sy r ) Read data (file). @@ -265,7 +256,8 @@ Inherit parent directory ACE to subdirectories. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY ( Sy i ) Only inherit, do not apply the permission on the directory itself. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT ( Sy n ) -Do not propagate inherit flags. Only first-level entries inherit ACLs. +Do not propagate inherit flags. +Only first-level entries inherit ACLs. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS ( Sy S ) Trigger alarm or audit on successful access. .It Dv ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS ( Sy F ) @@ -279,8 +271,8 @@ and .Fn archive_entry_acl_add_entry_w add a single ACL entry. For the access ACL and non-extended principals, the classic Unix permissions -are updated. An archive entry cannot contain both POSIX.1e and NFSv4 ACL -entries. +are updated. +An archive entry cannot contain both POSIX.1e and NFSv4 ACL entries. .Pp .Fn archive_entry_acl_clear removes all ACL entries and resets the enumeration pointer. @@ -300,7 +292,8 @@ for POSIX.1e ACLs and .It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT .It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM .El -for NFSv4 ACLs. For POSIX.1e ACLs if +for NFSv4 ACLs. +For POSIX.1e ACLs if .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS is included and at least one extended ACL entry is found, the three non-extended ACLs are added. @@ -312,7 +305,8 @@ add new .Pq or merge with existing ACL entries from .Pq wide -text. The argument +text. +The argument .Fa type may take one of the following values: .Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT" @@ -322,11 +316,13 @@ may take one of the following values: .El Supports all formats that can be created with .Fn archive_entry_acl_to_text -or respective +or respectively .Fn archive_entry_acl_to_text_w . -Existing ACL entries are preserved. To get a clean new ACL from text +Existing ACL entries are preserved. +To get a clean new ACL from text .Fn archive_entry_acl_clear -must be called first. Entries prefixed with +must be called first. +Entries prefixed with .Dq default: are treated as .Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT @@ -342,8 +338,6 @@ character are skipped. .Pp .Fn archive_entry_acl_next -and -.Fn archive_entry_acl_next_w return the next entry of the ACL list. This functions may only be called after .Fn archive_entry_acl_reset @@ -351,10 +345,8 @@ has indicated the presence of extended ACL entries. .Pp .Fn archive_entry_acl_reset prepare reading the list of ACL entries with -.Fn archive_entry_acl_next -or -.Fn archive_entry_acl_next_w . -The function returns either 0, if no non-extended ACLs are found. +.Fn archive_entry_acl_next . +The function returns 0 if no non-extended ACLs are found. In this case, the access permissions should be obtained by .Xr archive_entry_mode 3 or set using @@ -367,7 +359,8 @@ and .Fn archive_entry_acl_to_text_w convert the ACL entries for the given type into a .Pq wide -string of ACL entries separated by newline. If the pointer +string of ACL entries separated by newline. +If the pointer .Fa len_p is not NULL, then the function shall return the length of the string .Pq not including the NULL terminator @@ -415,7 +408,8 @@ are prefixed with .Dq default: . .Pp .Fn archive_entry_acl_types -get ACL entry types contained in an archive entry's ACL. As POSIX.1e and NFSv4 +get ACL entry types contained in an archive entry's ACL. +As POSIX.1e and NFSv4 ACL entries cannot be mixed, this function is a very efficient way to detect if an ACL already contains POSIX.1e or NFSv4 ACL entries. .Sh RETURN VALUES @@ -438,9 +432,7 @@ if all entries were successfully parsed and if one or more entries were invalid or non-parseable. .Pp .Fn archive_entry_acl_next -and -.Fn archive_entry_acl_next_w -return +returns .Dv ARCHIVE_OK on success, .Dv ARCHIVE_EOF diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_locale.h b/Utilities/cmlibarchive/libarchive/archive_entry_locale.h index 44550c51ec6..803c0368bb6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_locale.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry_locale.h @@ -25,13 +25,13 @@ * $FreeBSD$ */ +#ifndef ARCHIVE_ENTRY_LOCALE_H_INCLUDED +#define ARCHIVE_ENTRY_LOCALE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_ENTRY_LOCALE_H_INCLUDED -#define ARCHIVE_ENTRY_LOCALE_H_INCLUDED - struct archive_entry; struct archive_string_conv; diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_misc.3 b/Utilities/cmlibarchive/libarchive/archive_entry_misc.3 new file mode 100644 index 00000000000..dfab7ddb559 --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_entry_misc.3 @@ -0,0 +1,63 @@ +.\" Copyright (c) 2019 Martin Matuska +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd April 15, 2019 +.Dt ARCHIVE_ENTRY_MISC 3 +.Os +.Sh NAME +.Nm archive_entry_symlink_type , +.Nm archive_entry_set_symlink_type +.Nd miscellaneous functions for manipulating properties of archive_entry +.Sh LIBRARY +Streaming Archive Library (libarchive, -larchive) +.Sh SYNOPSIS +.In archive_entry.h +.Ft int +.Fn archive_entry_symlink_type "struct archive_entry *a" +.Ft void +.Fn archive_entry_set_symlink_type "struct archive_entry *a" "int" +.Sh DESCRIPTION +The function +.Fn archive_entry_symlink_type +returns and the function +.Fn archive_entry_set_symlink_type +sets the type of the symbolic link stored in an archive entry. +These functions +have special meaning on operating systems that support multiple symbolic link +types (e.g. Microsoft Windows). +.Pp +Supported values are: +.Bl -tag -width "AE_SYMLINK_TYPE_DIRECTORY" -compact +.It AE_SYMLINK_TYPE_UNDEFINED +Symbolic link target type is not defined (default on unix systems) +.It AE_SYMLINK_TYPE_FILE +Symbolic link points to a file +.It AE_SYMLINK_TYPE_DIRECTORY +Symbolic link points to a directory +.El +.Sh SEE ALSO +.Xr archive_entry 3 , +.Xr archive_entry_paths 3 , +.Xr archive_entry_stat 3 , +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_paths.3 b/Utilities/cmlibarchive/libarchive/archive_entry_paths.3 index f647212a98b..0f849c9ebb3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_paths.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry_paths.3 @@ -133,7 +133,7 @@ The accessor functions are named .Fn XXX_w . .It UTF-8 Unicode strings encoded as UTF-8. -This are convience functions to update both the multibyte and wide +These are convenience functions to update both the multibyte and wide character strings at the same time. .El .Pp @@ -141,13 +141,13 @@ The sourcepath is a pure filesystem concept and never stored in an archive directly. .Pp For that reason, it is only available as multibyte string. -The link path is a convience function for conditionally setting +The link path is a convenience function for conditionally setting hardlink or symlink destination. It doesn't have a corresponding get accessor function. .Pp .Fn archive_entry_set_XXX -is an alias for +is an alias for .Fn archive_entry_copy_XXX . .Sh SEE ALSO -.Xr archive_entry 3 -.Xr libarchive 3 , +.Xr archive_entry 3 , +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_perms.3 b/Utilities/cmlibarchive/libarchive/archive_entry_perms.3 index aae3648bb21..0291b7b4988 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_perms.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry_perms.3 @@ -126,7 +126,7 @@ The corresponding functions and .Fn archive_entry_set_perm store the given user id, group id and permission in the entry. -The permission is also set as side effect of calling +The permission is also set as a side effect of calling .Fn archive_entry_set_mode . .Pp .Fn archive_entry_strmode @@ -143,12 +143,12 @@ The accessor functions are named .Fn XXX_w . .It UTF-8 Unicode strings encoded as UTF-8. -This are convience functions to update both the multibyte and wide +These are convenience functions to update both the multibyte and wide character strings at the same time. .El .Pp .Fn archive_entry_set_XXX -is an alias for +is an alias for .Fn archive_entry_copy_XXX . .Ss File Flags File flags are transparently converted between a bitmap @@ -182,7 +182,7 @@ The .Fn archive_entry_copy_fflags_text and .Fn archive_entry_copy_fflags_text_w -functions parse the provided text and sets the internal bitmap values. +functions parse the provided text and set the internal bitmap values. This is a platform-specific operation; names that are not meaningful on the current platform will be ignored. The function returns a pointer to the start of the first name that was not @@ -197,8 +197,8 @@ which stops parsing at the first unrecognized name.) .Xr archive_entry 3 , .Xr archive_entry_acl 3 , .Xr archive_read_disk 3 , -.Xr archive_write_disk 3 -.Xr libarchive 3 , +.Xr archive_write_disk 3 , +.Xr libarchive 3 .Sh BUGS The platform types .Vt uid_t diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_private.h b/Utilities/cmlibarchive/libarchive/archive_entry_private.h index c69233e68bd..2b9a084ca15 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry_private.h @@ -25,13 +25,13 @@ * $FreeBSD: head/lib/libarchive/archive_entry_private.h 201096 2009-12-28 02:41:27Z kientzle $ */ +#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED +#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED -#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED - #include "archive_acl_private.h" #include "archive_string.h" @@ -176,6 +176,9 @@ struct archive_entry { /* Miscellaneous. */ char strmode[12]; + + /* Symlink type support */ + int ae_symlink_type; }; #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_stat.3 b/Utilities/cmlibarchive/libarchive/archive_entry_stat.3 index 26611e4c62e..aa5c8e03f9f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_stat.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry_stat.3 @@ -54,7 +54,7 @@ .Nm archive_entry_rdevmajor , .Nm archive_entry_set_rdevmajor , .Nm archive_entry_rdevminor , -.Nm archive_entry_set_rdevminor , +.Nm archive_entry_set_rdevminor .Nd accessor functions for manipulating archive entry descriptions .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) @@ -267,8 +267,8 @@ platforms. Some archive formats use the combined form, while other formats use the split form. .Sh SEE ALSO +.Xr stat 2 , .Xr archive_entry_acl 3 , .Xr archive_entry_perms 3 , .Xr archive_entry_time 3 , -.Xr libarchive 3 , -.Xr stat 2 +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_time.3 b/Utilities/cmlibarchive/libarchive/archive_entry_time.3 index 18645215937..d0563eaef43 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_time.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry_time.3 @@ -48,7 +48,7 @@ .Nm archive_entry_mtime_nsec , .Nm archive_entry_mtime_is_set , .Nm archive_entry_set_mtime , -.Nm archive_entry_unset_mtime , +.Nm archive_entry_unset_mtime .Nd functions for manipulating times in archive entry descriptions .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) @@ -113,8 +113,8 @@ The current state can be queried using .Fn XXX_is_set . Unset time fields have a second and nanosecond field of 0. .Sh SEE ALSO -.Xr archive_entry 3 -.Xr libarchive 3 , +.Xr archive_entry 3 , +.Xr libarchive 3 .Sh HISTORY The .Nm libarchive diff --git a/Utilities/cmlibarchive/libarchive/archive_getdate.c b/Utilities/cmlibarchive/libarchive/archive_getdate.c index 030c083ce71..6786d35d5ec 100644 --- a/Utilities/cmlibarchive/libarchive/archive_getdate.c +++ b/Utilities/cmlibarchive/libarchive/archive_getdate.c @@ -27,6 +27,9 @@ ** This code is in the public domain and has no copyright. */ +#ifndef CM_GET_DATE +#include "archive_platform.h" +#endif #ifdef __FreeBSD__ #include __FBSDID("$FreeBSD$"); @@ -694,8 +697,16 @@ Convert(time_t Month, time_t Day, time_t Year, signed char DaysInMonth[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - time_t Julian; - int i; + time_t Julian; + int i; + struct tm *ltime; +#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) + struct tm tmbuf; +#endif +#if defined(HAVE__LOCALTIME64_S) + errno_t terr; + __time64_t tmptime; +#endif if (Year < 69) Year += 2000; @@ -722,21 +733,64 @@ Convert(time_t Month, time_t Day, time_t Year, Julian *= DAY; Julian += Timezone; Julian += Hours * HOUR + Minutes * MINUTE + Seconds; +#if defined(HAVE_LOCALTIME_R) + ltime = localtime_r(&Julian, &tmbuf); +#elif defined(HAVE__LOCALTIME64_S) + tmptime = Julian; + terr = _localtime64_s(&tmbuf, &tmptime); + if (terr) + ltime = NULL; + else + ltime = &tmbuf; +#else + ltime = localtime(&Julian); +#endif if (DSTmode == DSTon - || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + || (DSTmode == DSTmaybe && ltime->tm_isdst)) Julian -= HOUR; return Julian; } - static time_t DSTcorrect(time_t Start, time_t Future) { - time_t StartDay; - time_t FutureDay; + time_t StartDay; + time_t FutureDay; + struct tm *ltime; +#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) + struct tm tmbuf; +#endif +#if defined(HAVE__LOCALTIME64_S) + errno_t terr; + __time64_t tmptime; +#endif - StartDay = (localtime(&Start)->tm_hour + 1) % 24; - FutureDay = (localtime(&Future)->tm_hour + 1) % 24; +#if defined(HAVE_LOCALTIME_R) + ltime = localtime_r(&Start, &tmbuf); +#elif defined(HAVE__LOCALTIME64_S) + tmptime = Start; + terr = _localtime64_s(&tmbuf, &tmptime); + if (terr) + ltime = NULL; + else + ltime = &tmbuf; +#else + ltime = localtime(&Start); +#endif + StartDay = (ltime->tm_hour + 1) % 24; +#if defined(HAVE_LOCALTIME_R) + ltime = localtime_r(&Future, &tmbuf); +#elif defined(HAVE__LOCALTIME64_S) + tmptime = Future; + terr = _localtime64_s(&tmbuf, &tmptime); + if (terr) + ltime = NULL; + else + ltime = &tmbuf; +#else + ltime = localtime(&Future); +#endif + FutureDay = (ltime->tm_hour + 1) % 24; return (Future - Start) + (StartDay - FutureDay) * HOUR; } @@ -747,9 +801,27 @@ RelativeDate(time_t Start, time_t zone, int dstmode, { struct tm *tm; time_t t, now; +#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) + struct tm tmbuf; +#endif +#if defined(HAVE__GMTIME64_S) + errno_t terr; + __time64_t tmptime; +#endif t = Start - zone; +#if defined(HAVE_GMTIME_R) + tm = gmtime_r(&t, &tmbuf); +#elif defined(HAVE__GMTIME64_S) + tmptime = t; + terr = _gmtime64_s(&tmbuf, &tmptime); + if (terr) + tm = NULL; + else + tm = &tmbuf; +#else tm = gmtime(&t); +#endif now = Start; now += DAY * ((DayNumber - tm->tm_wday + 7) % 7); now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); @@ -765,10 +837,28 @@ RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) struct tm *tm; time_t Month; time_t Year; +#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) + struct tm tmbuf; +#endif +#if defined(HAVE__LOCALTIME64_S) + errno_t terr; + __time64_t tmptime; +#endif if (RelMonth == 0) return 0; +#if defined(HAVE_LOCALTIME_R) + tm = localtime_r(&Start, &tmbuf); +#elif defined(HAVE__LOCALTIME64_S) + tmptime = Start; + terr = _localtime64_s(&tmbuf, &tmptime); + if (terr) + tm = NULL; + else + tm = &tmbuf; +#else tm = localtime(&Start); +#endif Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; Year = Month / 12; Month = Month % 12 + 1; @@ -905,6 +995,10 @@ __archive_get_date(time_t now, const char *p) time_t Start; time_t tod; long tzone; +#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S) + errno_t terr; + __time64_t tmptime; +#endif /* Clear out the parsed token array. */ memset(tokens, 0, sizeof(tokens)); @@ -913,20 +1007,44 @@ __archive_get_date(time_t now, const char *p) gds = &_gds; /* Look up the current time. */ +#if defined(HAVE_LOCALTIME_R) + tm = localtime_r(&now, &local); +#elif defined(HAVE__LOCALTIME64_S) + tmptime = now; + terr = _localtime64_s(&local, &tmptime); + if (terr) + tm = NULL; + else + tm = &local; +#else memset(&local, 0, sizeof(local)); - tm = localtime (&now); + tm = localtime(&now); +#endif if (tm == NULL) return -1; +#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S) local = *tm; +#endif /* Look up UTC if we can and use that to determine the current * timezone offset. */ +#if defined(HAVE_GMTIME_R) + gmt_ptr = gmtime_r(&now, &gmt); +#elif defined(HAVE__GMTIME64_S) + tmptime = now; + terr = _gmtime64_s(&gmt, &tmptime); + if (terr) + gmt_ptr = NULL; + else + gmt_ptr = &gmt; +#else memset(&gmt, 0, sizeof(gmt)); - gmt_ptr = gmtime (&now); + gmt_ptr = gmtime(&now); if (gmt_ptr != NULL) { /* Copy, in case localtime and gmtime use the same buffer. */ gmt = *gmt_ptr; } +#endif if (gmt_ptr != NULL) tzone = difftm (&gmt, &local); else @@ -960,7 +1078,18 @@ __archive_get_date(time_t now, const char *p) * time components instead of the local timezone. */ if (gds->HaveZone && gmt_ptr != NULL) { now -= gds->Timezone; - gmt_ptr = gmtime (&now); +#if defined(HAVE_GMTIME_R) + gmt_ptr = gmtime_r(&now, &gmt); +#elif defined(HAVE__GMTIME64_S) + tmptime = now; + terr = _gmtime64_s(&gmt, &tmptime); + if (terr) + gmt_ptr = NULL; + else + gmt_ptr = &gmt; +#else + gmt_ptr = gmtime(&now); +#endif if (gmt_ptr != NULL) local = *gmt_ptr; now += gds->Timezone; diff --git a/Utilities/cmlibarchive/libarchive/archive_getdate.h b/Utilities/cmlibarchive/libarchive/archive_getdate.h index 666ff5ff78b..900a8f692e9 100644 --- a/Utilities/cmlibarchive/libarchive/archive_getdate.h +++ b/Utilities/cmlibarchive/libarchive/archive_getdate.h @@ -25,13 +25,13 @@ * $FreeBSD$ */ +#ifndef ARCHIVE_GETDATE_H_INCLUDED +#define ARCHIVE_GETDATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_GETDATE_H_INCLUDED -#define ARCHIVE_GETDATE_H_INCLUDED - #include time_t __archive_get_date(time_t now, const char *); diff --git a/Utilities/cmlibarchive/libarchive/archive_hmac.c b/Utilities/cmlibarchive/libarchive/archive_hmac.c index f29965577f0..2a9d04c8d8f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_hmac.c +++ b/Utilities/cmlibarchive/libarchive/archive_hmac.c @@ -83,6 +83,9 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif BCRYPT_ALG_HANDLE hAlg; BCRYPT_HASH_HANDLE hHash; DWORD hash_len; @@ -147,6 +150,53 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) } } +#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H) + +static int +__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) +{ + const mbedtls_md_info_t *info; + int ret; + + mbedtls_md_init(ctx); + info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); + if (info == NULL) { + mbedtls_md_free(ctx); + return (-1); + } + ret = mbedtls_md_setup(ctx, info, 1); + if (ret != 0) { + mbedtls_md_free(ctx); + return (-1); + } + ret = mbedtls_md_hmac_starts(ctx, key, key_len); + if (ret != 0) { + mbedtls_md_free(ctx); + return (-1); + } + return 0; +} + +static void +__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, + size_t data_len) +{ + mbedtls_md_hmac_update(ctx, data, data_len); +} + +static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) +{ + (void)out_len; /* UNUSED */ + + mbedtls_md_hmac_finish(ctx, out); +} + +static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) +{ + mbedtls_md_free(ctx); + memset(ctx, 0, sizeof(*ctx)); +} + #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H) static int @@ -198,6 +248,7 @@ static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) { unsigned int len = (unsigned int)*out_len; + HMAC_Final(*ctx, out, &len); *out_len = len; } diff --git a/Utilities/cmlibarchive/libarchive/archive_hmac_private.h b/Utilities/cmlibarchive/libarchive/archive_hmac_private.h index eb45c4ef21b..13a67d4955a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_hmac_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_hmac_private.h @@ -23,13 +23,12 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - #ifndef ARCHIVE_HMAC_PRIVATE_H_INCLUDED #define ARCHIVE_HMAC_PRIVATE_H_INCLUDED +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif /* * On systems that do not support any recognized crypto libraries, * the archive_hmac.c file is expected to define no usable symbols. @@ -64,6 +63,11 @@ typedef struct { } archive_hmac_sha1_ctx; +#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H) +#include + +typedef mbedtls_md_context_t archive_hmac_sha1_ctx; + #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H) #include diff --git a/Utilities/cmlibarchive/libarchive/archive_match.c b/Utilities/cmlibarchive/libarchive/archive_match.c index f150e8224c1..04747b1f666 100644 --- a/Utilities/cmlibarchive/libarchive/archive_match.c +++ b/Utilities/cmlibarchive/libarchive/archive_match.c @@ -93,6 +93,9 @@ struct archive_match { /* exclusion/inclusion set flag. */ int setflag; + /* Recursively include directory content? */ + int recursive_include; + /* * Matching filename patterns. */ @@ -223,6 +226,7 @@ archive_match_new(void) return (NULL); a->archive.magic = ARCHIVE_MATCH_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; + a->recursive_include = 1; match_list_init(&(a->inclusions)); match_list_init(&(a->exclusions)); __archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs); @@ -470,6 +474,28 @@ archive_match_path_excluded(struct archive *_a, #endif } +/* + * When recursive inclusion of directory content is enabled, + * an inclusion pattern that matches a directory will also + * include everything beneath that directory. Enabled by default. + * + * For compatibility with GNU tar, exclusion patterns always + * match if a subset of the full patch matches (i.e., they are + * are not rooted at the beginning of the path) and thus there + * is no corresponding non-recursive exclusion mode. + */ +int +archive_match_set_inclusion_recursion(struct archive *_a, int enabled) +{ + struct archive_match *a; + + archive_check_magic(_a, ARCHIVE_MATCH_MAGIC, + ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion"); + a = (struct archive_match *)_a; + a->recursive_include = enabled; + return (ARCHIVE_OK); +} + /* * Utility functions to get statistic information for inclusion patterns. */ @@ -781,7 +807,10 @@ static int match_path_inclusion(struct archive_match *a, struct match *m, int mbs, const void *pn) { - int flag = PATHMATCH_NO_ANCHOR_END; + /* Recursive operation requires only a prefix match. */ + int flag = a->recursive_include ? + PATHMATCH_NO_ANCHOR_END : + 0; int r; if (mbs) { @@ -1232,7 +1261,7 @@ set_timefilter_pathname_mbs(struct archive_match *a, int timetype, archive_set_error(&(a->archive), EINVAL, "pathname is empty"); return (ARCHIVE_FAILED); } - if (stat(path, &st) != 0) { + if (la_stat(path, &st) != 0) { archive_set_error(&(a->archive), errno, "Failed to stat()"); return (ARCHIVE_FAILED); } diff --git a/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h b/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h index 43a3ccc52a1..ebb06702d0c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h @@ -22,9 +22,14 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED #define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + #include #include diff --git a/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h b/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h index 921249bb945..25c8dda654f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h @@ -22,9 +22,14 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #ifndef ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED #define ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + #include #include diff --git a/Utilities/cmlibarchive/libarchive/archive_options_private.h b/Utilities/cmlibarchive/libarchive/archive_options_private.h index 6ef0165aff6..9a7f8080d2f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_options_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_options_private.h @@ -23,6 +23,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED +#define ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED + #include "archive_platform.h" __FBSDID("$FreeBSD$"); @@ -45,3 +48,4 @@ _archive_set_either_option(struct archive *a, const char *m, const char *o, const char *v, option_handler use_format_option, option_handler use_filter_option); +#endif diff --git a/Utilities/cmlibarchive/libarchive/archive_pack_dev.h b/Utilities/cmlibarchive/libarchive/archive_pack_dev.h index 749fd3d2cb6..eaf23e3883e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_pack_dev.h +++ b/Utilities/cmlibarchive/libarchive/archive_pack_dev.h @@ -31,8 +31,8 @@ /* Originally from NetBSD's mknod(8) source. */ -#ifndef _PACK_DEV_H -#define _PACK_DEV_H +#ifndef ARCHIVE_PACK_DEV_H +#define ARCHIVE_PACK_DEV_H typedef dev_t pack_t(int, unsigned long [], const char **); @@ -46,4 +46,4 @@ pack_t pack_native; (((y) << 12) & 0xfff00000) | \ (((y) << 0) & 0x000000ff))) -#endif /* _PACK_DEV_H */ +#endif /* ARCHIVE_PACK_DEV_H */ diff --git a/Utilities/cmlibarchive/libarchive/archive_pathmatch.h b/Utilities/cmlibarchive/libarchive/archive_pathmatch.h index e6901774ddd..9995142921e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_pathmatch.h +++ b/Utilities/cmlibarchive/libarchive/archive_pathmatch.h @@ -26,15 +26,15 @@ * $FreeBSD$ */ +#ifndef ARCHIVE_PATHMATCH_H +#define ARCHIVE_PATHMATCH_H + #ifndef __LIBARCHIVE_BUILD #ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif #endif -#ifndef ARCHIVE_PATHMATCH_H -#define ARCHIVE_PATHMATCH_H - /* Don't anchor at beginning unless the pattern starts with "^" */ #define PATHMATCH_NO_ANCHOR_START 1 /* Don't anchor at end unless the pattern ends with "$" */ diff --git a/Utilities/cmlibarchive/libarchive/archive_platform.h b/Utilities/cmlibarchive/libarchive/archive_platform.h index e161e640617..32739301d92 100644 --- a/Utilities/cmlibarchive/libarchive/archive_platform.h +++ b/Utilities/cmlibarchive/libarchive/archive_platform.h @@ -69,6 +69,8 @@ * either Windows or Posix APIs. */ #if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) #include "archive_windows.h" +#else +#define la_stat(path,stref) stat(path,stref) #endif /* @@ -105,56 +107,44 @@ /* Borland warns about its own constants! */ #if defined(__BORLANDC__) -# if HAVE_DECL_UINT64_MAX -# undef UINT64_MAX -# undef HAVE_DECL_UINT64_MAX -# endif -# if HAVE_DECL_UINT64_MIN -# undef UINT64_MIN -# undef HAVE_DECL_UINT64_MIN -# endif -# if HAVE_DECL_INT64_MAX -# undef INT64_MAX -# undef HAVE_DECL_INT64_MAX -# endif -# if HAVE_DECL_INT64_MIN -# undef INT64_MIN -# undef HAVE_DECL_INT64_MIN -# endif +# undef UINT64_MAX +# undef UINT64_MIN +# undef INT64_MAX +# undef INT64_MIN #endif /* Some platforms lack the standard *_MAX definitions. */ -#if !HAVE_DECL_SIZE_MAX +#ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif -#if !HAVE_DECL_SSIZE_MAX +#ifndef SSIZE_MAX #define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1)) #endif -#if !HAVE_DECL_UINT32_MAX +#ifndef UINT32_MAX #define UINT32_MAX (~(uint32_t)0) #endif -#if !HAVE_DECL_INT32_MAX +#ifndef INT32_MAX #define INT32_MAX ((int32_t)(UINT32_MAX >> 1)) #endif -#if !HAVE_DECL_INT32_MIN +#ifndef INT32_MIN #define INT32_MIN ((int32_t)(~INT32_MAX)) #endif -#if !HAVE_DECL_UINT64_MAX +#ifndef UINT64_MAX #define UINT64_MAX (~(uint64_t)0) #endif -#if !HAVE_DECL_INT64_MAX +#ifndef INT64_MAX #define INT64_MAX ((int64_t)(UINT64_MAX >> 1)) #endif -#if !HAVE_DECL_INT64_MIN +#ifndef INT64_MIN #define INT64_MIN ((int64_t)(~INT64_MAX)) #endif -#if !HAVE_DECL_UINTMAX_MAX +#ifndef UINTMAX_MAX #define UINTMAX_MAX (~(uintmax_t)0) #endif -#if !HAVE_DECL_INTMAX_MAX +#ifndef INTMAX_MAX #define INTMAX_MAX ((intmax_t)(UINTMAX_MAX >> 1)) #endif -#if !HAVE_DECL_INTMAX_MIN +#ifndef INTMAX_MIN #define INTMAX_MIN ((intmax_t)(~INTMAX_MAX)) #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_platform_acl.h b/Utilities/cmlibarchive/libarchive/archive_platform_acl.h index 3498f78b3c8..264e6de375a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_platform_acl.h +++ b/Utilities/cmlibarchive/libarchive/archive_platform_acl.h @@ -30,6 +30,12 @@ #ifndef ARCHIVE_PLATFORM_ACL_H_INCLUDED #define ARCHIVE_PLATFORM_ACL_H_INCLUDED +#ifndef __LIBARCHIVE_BUILD +#ifndef __LIBARCHIVE_TEST_COMMON +#error This header is only to be used internally to libarchive. +#endif +#endif + /* * Determine what ACL types are supported */ diff --git a/Utilities/cmlibarchive/libarchive/archive_platform_xattr.h b/Utilities/cmlibarchive/libarchive/archive_platform_xattr.h index 4edfecfdbdf..ad4b90ab7b2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_platform_xattr.h +++ b/Utilities/cmlibarchive/libarchive/archive_platform_xattr.h @@ -30,6 +30,12 @@ #ifndef ARCHIVE_PLATFORM_XATTR_H_INCLUDED #define ARCHIVE_PLATFORM_XATTR_H_INCLUDED +#ifndef __LIBARCHIVE_BUILD +#ifndef __LIBARCHIVE_TEST_COMMON +#error This header is only to be used internally to libarchive. +#endif +#endif + /* * Determine if we support extended attributes */ diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd7.c b/Utilities/cmlibarchive/libarchive/archive_ppmd7.c index d0bacc68cd7..4029395b4c7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_ppmd7.c +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd7.c @@ -1000,7 +1000,7 @@ static void RangeEnc_ShiftLow(CPpmd7z_RangeEnc *p) static void RangeEnc_Encode(CPpmd7z_RangeEnc *p, UInt32 start, UInt32 size, UInt32 total) { - p->Low += start * (p->Range /= total); + p->Low += (UInt64)start * (UInt64)(p->Range /= total); p->Range *= size; while (p->Range < kTopValue) { diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h b/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h index 577d6fb43d0..71b954458c6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h @@ -6,13 +6,13 @@ This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H. If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */ +#ifndef ARCHIVE_PPMD7_PRIVATE_H_INCLUDED +#define ARCHIVE_PPMD7_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_PPMD7_PRIVATE_H_INCLUDED -#define ARCHIVE_PPMD7_PRIVATE_H_INCLUDED - #include "archive_ppmd_private.h" #define PPMD7_MIN_ORDER 2 diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd8.c b/Utilities/cmlibarchive/libarchive/archive_ppmd8.c new file mode 100644 index 00000000000..d1779395dac --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd8.c @@ -0,0 +1,1287 @@ +/* Ppmd8.c -- PPMdI codec +2016-05-21 : Igor Pavlov : Public domain +This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */ + +#include "archive_platform.h" + +#include + +#include "archive_ppmd8_private.h" + +const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; +static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; + +#define MAX_FREQ 124 +#define UNIT_SIZE 12 + +#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) +#define U2I(nu) (p->Units2Indx[(nu) - 1]) +#define I2U(indx) (p->Indx2Units[indx]) + +#ifdef PPMD_32BIT + #define REF(ptr) (ptr) +#else + #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base)) +#endif + +#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr)) + +#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref)) +#define STATS(ctx) Ppmd8_GetStats(p, ctx) +#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx) +#define SUFFIX(ctx) CTX((ctx)->Suffix) + +#define kTop (1 << 24) +#define kBot (1 << 15) + +typedef CPpmd8_Context * CTX_PTR; + +struct CPpmd8_Node_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd8_Node_ * + #else + UInt32 + #endif + CPpmd8_Node_Ref; + +typedef struct CPpmd8_Node_ +{ + UInt32 Stamp; + CPpmd8_Node_Ref Next; + UInt32 NU; +} CPpmd8_Node; + +#ifdef PPMD_32BIT + #define NODE(ptr) (ptr) +#else + #define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs))) +#endif + +#define EMPTY_NODE 0xFFFFFFFF + +void Ppmd8_Construct(CPpmd8 *p) +{ + unsigned i, k, m; + + p->Base = 0; + + for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++) + { + unsigned step = (i >= 12 ? 4 : (i >> 2) + 1); + do { p->Units2Indx[k++] = (Byte)i; } while (--step); + p->Indx2Units[i] = (Byte)k; + } + + p->NS2BSIndx[0] = (0 << 1); + p->NS2BSIndx[1] = (1 << 1); + memset(p->NS2BSIndx + 2, (2 << 1), 9); + memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11); + + for (i = 0; i < 5; i++) + p->NS2Indx[i] = (Byte)i; + for (m = i, k = 1; i < 260; i++) + { + p->NS2Indx[i] = (Byte)m; + if (--k == 0) + k = (++m) - 4; + } +} + +void Ppmd8_Free(CPpmd8 *p) +{ + free(p->Base); + p->Size = 0; + p->Base = 0; +} + +Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size) +{ + if (p->Base == 0 || p->Size != size) + { + Ppmd8_Free(p); + p->AlignOffset = + #ifdef PPMD_32BIT + (4 - size) & 3; + #else + 4 - (size & 3); + #endif + if ((p->Base = (Byte *)malloc(p->AlignOffset + size)) == 0) + return False; + p->Size = size; + } + return True; +} + +static void InsertNode(CPpmd8 *p, void *node, unsigned indx) +{ + ((CPpmd8_Node *)node)->Stamp = EMPTY_NODE; + ((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx]; + ((CPpmd8_Node *)node)->NU = I2U(indx); + p->FreeList[indx] = REF(node); + p->Stamps[indx]++; +} + +static void *RemoveNode(CPpmd8 *p, unsigned indx) +{ + CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]); + p->FreeList[indx] = node->Next; + p->Stamps[indx]--; + return node; +} + +static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx) +{ + unsigned i, nu = I2U(oldIndx) - I2U(newIndx); + ptr = (Byte *)ptr + U2B(I2U(newIndx)); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1); + } + InsertNode(p, ptr, i); +} + +static void GlueFreeBlocks(CPpmd8 *p) +{ + CPpmd8_Node_Ref head = 0; + CPpmd8_Node_Ref *prev = &head; + unsigned i; + + p->GlueCount = 1 << 13; + memset(p->Stamps, 0, sizeof(p->Stamps)); + + /* Order-0 context is always at top UNIT, so we don't need guard NODE at the end. + All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */ + if (p->LoUnit != p->HiUnit) + ((CPpmd8_Node *)p->LoUnit)->Stamp = 0; + + /* Glue free blocks */ + for (i = 0; i < PPMD_NUM_INDEXES; i++) + { + CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i]; + p->FreeList[i] = 0; + while (next != 0) + { + CPpmd8_Node *node = NODE(next); + if (node->NU != 0) + { + CPpmd8_Node *node2; + *prev = next; + prev = &(node->Next); + while ((node2 = node + node->NU)->Stamp == EMPTY_NODE) + { + node->NU += node2->NU; + node2->NU = 0; + } + } + next = node->Next; + } + } + *prev = 0; + + /* Fill lists of free blocks */ + while (head != 0) + { + CPpmd8_Node *node = NODE(head); + unsigned nu; + head = node->Next; + nu = node->NU; + if (nu == 0) + continue; + for (; nu > 128; nu -= 128, node += 128) + InsertNode(p, node, PPMD_NUM_INDEXES - 1); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, node + k, nu - k - 1); + } + InsertNode(p, node, i); + } +} + +static void *AllocUnitsRare(CPpmd8 *p, unsigned indx) +{ + unsigned i; + void *retVal; + if (p->GlueCount == 0) + { + GlueFreeBlocks(p); + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + } + i = indx; + do + { + if (++i == PPMD_NUM_INDEXES) + { + UInt32 numBytes = U2B(I2U(indx)); + p->GlueCount--; + return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL); + } + } + while (p->FreeList[i] == 0); + retVal = RemoveNode(p, i); + SplitBlock(p, retVal, i, indx); + return retVal; +} + +static void *AllocUnits(CPpmd8 *p, unsigned indx) +{ + UInt32 numBytes; + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + numBytes = U2B(I2U(indx)); + if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit)) + { + void *retVal = p->LoUnit; + p->LoUnit += numBytes; + return retVal; + } + return AllocUnitsRare(p, indx); +} + +#define MyMem12Cpy(dest, src, num) \ + { UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \ + do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); } + +static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU) +{ + unsigned i0 = U2I(oldNU); + unsigned i1 = U2I(newNU); + if (i0 == i1) + return oldPtr; + if (p->FreeList[i1] != 0) + { + void *ptr = RemoveNode(p, i1); + MyMem12Cpy(ptr, oldPtr, newNU); + InsertNode(p, oldPtr, i0); + return ptr; + } + SplitBlock(p, oldPtr, i0, i1); + return oldPtr; +} + +static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu) +{ + InsertNode(p, ptr, U2I(nu)); +} + +static void SpecialFreeUnit(CPpmd8 *p, void *ptr) +{ + if ((Byte *)ptr != p->UnitsStart) + InsertNode(p, ptr, 0); + else + { + #ifdef PPMD8_FREEZE_SUPPORT + *(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */ + #endif + p->UnitsStart += UNIT_SIZE; + } +} + +static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu) +{ + unsigned indx = U2I(nu); + void *ptr; + if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx]) + return oldPtr; + ptr = RemoveNode(p, indx); + MyMem12Cpy(ptr, oldPtr, nu); + if ((Byte*)oldPtr != p->UnitsStart) + InsertNode(p, oldPtr, indx); + else + p->UnitsStart += U2B(I2U(indx)); + return ptr; +} + +static void ExpandTextArea(CPpmd8 *p) +{ + UInt32 count[PPMD_NUM_INDEXES]; + unsigned i; + memset(count, 0, sizeof(count)); + if (p->LoUnit != p->HiUnit) + ((CPpmd8_Node *)p->LoUnit)->Stamp = 0; + + { + CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart; + for (; node->Stamp == EMPTY_NODE; node += node->NU) + { + node->Stamp = 0; + count[U2I(node->NU)]++; + } + p->UnitsStart = (Byte *)node; + } + + for (i = 0; i < PPMD_NUM_INDEXES; i++) + { + CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i]; + while (count[i] != 0) + { + CPpmd8_Node *node = NODE(*next); + while (node->Stamp == 0) + { + *next = node->Next; + node = NODE(*next); + p->Stamps[i]--; + if (--count[i] == 0) + break; + } + next = &node->Next; + } + } +} + +#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16))) + +static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v) +{ + (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF); + (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF); +} + +#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); } + +static void RestartModel(CPpmd8 *p) +{ + unsigned i, k, m, r; + + memset(p->FreeList, 0, sizeof(p->FreeList)); + memset(p->Stamps, 0, sizeof(p->Stamps)); + RESET_TEXT(0); + p->HiUnit = p->Text + p->Size; + p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE; + p->GlueCount = 0; + + p->OrderFall = p->MaxOrder; + p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1; + p->PrevSuccess = 0; + + p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */ + p->MinContext->Suffix = 0; + p->MinContext->NumStats = 255; + p->MinContext->Flags = 0; + p->MinContext->SummFreq = 256 + 1; + p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */ + p->LoUnit += U2B(256 / 2); + p->MinContext->Stats = REF(p->FoundState); + for (i = 0; i < 256; i++) + { + CPpmd_State *s = &p->FoundState[i]; + s->Symbol = (Byte)i; + s->Freq = 1; + SetSuccessor(s, 0); + } + + for (i = m = 0; m < 25; m++) + { + while (p->NS2Indx[i] == m) + i++; + for (k = 0; k < 8; k++) + { + UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1)); + UInt16 *dest = p->BinSumm[m] + k; + for (r = 0; r < 64; r += 8) + dest[r] = val; + } + } + + for (i = m = 0; m < 24; m++) + { + while (p->NS2Indx[i + 3] == m + 3) + i++; + for (k = 0; k < 32; k++) + { + CPpmd_See *s = &p->See[m][k]; + s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4)); + s->Count = 7; + } + } +} + +void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod) +{ + p->MaxOrder = maxOrder; + p->RestoreMethod = restoreMethod; + RestartModel(p); + p->DummySee.Shift = PPMD_PERIOD_BITS; + p->DummySee.Summ = 0; /* unused */ + p->DummySee.Count = 64; /* unused */ +} + +static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale) +{ + unsigned i = ctx->NumStats, escFreq, sumFreq, flags; + CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1); + ctx->Stats = REF(s); + #ifdef PPMD8_FREEZE_SUPPORT + /* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */ + scale |= (ctx->SummFreq >= ((UInt32)1 << 15)); + #endif + flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40); + escFreq = ctx->SummFreq - s->Freq; + sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale)); + do + { + escFreq -= (++s)->Freq; + sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale)); + flags |= 0x08 * (s->Symbol >= 0x40); + } + while (--i); + ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale)); + ctx->Flags = (Byte)flags; +} + +static void SwapStates(CPpmd_State *t1, CPpmd_State *t2) +{ + CPpmd_State tmp = *t1; + *t1 = *t2; + *t2 = tmp; +} + +static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order) +{ + int i; + unsigned tmp; + CPpmd_State *s; + + if (!ctx->NumStats) + { + s = ONE_STATE(ctx); + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart) + { + if (order < p->MaxOrder) + SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + if (SUCCESSOR(s) || order <= 9) /* O_BOUND */ + return REF(ctx); + } + SpecialFreeUnit(p, ctx); + return 0; + } + + ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1)); + + for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--) + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart) + { + CPpmd_State *s2 = STATS(ctx) + (i--); + SetSuccessor(s, 0); + SwapStates(s, s2); + } + else if (order < p->MaxOrder) + SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + + if (i != ctx->NumStats && order) + { + ctx->NumStats = (Byte)i; + s = STATS(ctx); + if (i < 0) + { + FreeUnits(p, s, tmp); + SpecialFreeUnit(p, ctx); + return 0; + } + if (i == 0) + { + ctx->Flags = (Byte)((ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40)); + *ONE_STATE(ctx) = *s; + FreeUnits(p, s, tmp); + /* 9.31: the code was fixed. It's was not BUG, if Freq <= MAX_FREQ = 124 */ + ONE_STATE(ctx)->Freq = (Byte)(((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3); + } + else + Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i); + } + return REF(ctx); +} + +#ifdef PPMD8_FREEZE_SUPPORT +static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order) +{ + CPpmd_State *s; + if (!ctx->NumStats) + { + s = ONE_STATE(ctx); + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder) + SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + /* Suffix context can be removed already, since different (high-order) + Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */ + if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF)) + { + FreeUnits(p, ctx, 1); + return 0; + } + else + return REF(ctx); + } + + for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--) + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder) + SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + + return REF(ctx); +} +#endif + +static UInt32 GetUsedMemory(const CPpmd8 *p) +{ + UInt32 v = 0; + unsigned i; + for (i = 0; i < PPMD_NUM_INDEXES; i++) + v += p->Stamps[i] * I2U(i); + return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v); +} + +#ifdef PPMD8_FREEZE_SUPPORT + #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor) +#else + #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1) +#endif + +static void RestoreModel(CPpmd8 *p, CTX_PTR c1 + #ifdef PPMD8_FREEZE_SUPPORT + , CTX_PTR fSuccessor + #endif + ) +{ + CTX_PTR c; + CPpmd_State *s; + RESET_TEXT(0); + for (c = p->MaxContext; c != c1; c = SUFFIX(c)) + if (--(c->NumStats) == 0) + { + s = STATS(c); + c->Flags = (Byte)((c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40)); + *ONE_STATE(c) = *s; + SpecialFreeUnit(p, s); + ONE_STATE(c)->Freq = (Byte)(((unsigned)ONE_STATE(c)->Freq + 11) >> 3); + } + else + Refresh(p, c, (c->NumStats+3) >> 1, 0); + + for (; c != p->MinContext; c = SUFFIX(c)) + if (!c->NumStats) + ONE_STATE(c)->Freq = (Byte)(ONE_STATE(c)->Freq - (ONE_STATE(c)->Freq >> 1)); + else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats) + Refresh(p, c, (c->NumStats + 2) >> 1, 1); + + #ifdef PPMD8_FREEZE_SUPPORT + if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + p->MaxContext = fSuccessor; + p->GlueCount += !(p->Stamps[1] & 1); + } + else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE) + { + while (p->MaxContext->Suffix) + p->MaxContext = SUFFIX(p->MaxContext); + RemoveBinContexts(p, p->MaxContext, 0); + p->RestoreMethod++; + p->GlueCount = 0; + p->OrderFall = p->MaxOrder; + } + else + #endif + if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1)) + RestartModel(p); + else + { + while (p->MaxContext->Suffix) + p->MaxContext = SUFFIX(p->MaxContext); + do + { + CutOff(p, p->MaxContext, 0); + ExpandTextArea(p); + } + while (GetUsedMemory(p) > 3 * (p->Size >> 2)); + p->GlueCount = 0; + p->OrderFall = p->MaxOrder; + } +} + +static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c) +{ + CPpmd_State upState; + Byte flags; + CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState); + /* fixed over Shkarin's code. Maybe it could work without + 1 too. */ + CPpmd_State *ps[PPMD8_MAX_ORDER + 1]; + unsigned numPs = 0; + + if (!skip) + ps[numPs++] = p->FoundState; + + while (c->Suffix) + { + CPpmd_Void_Ref successor; + CPpmd_State *s; + c = SUFFIX(c); + if (s1) + { + s = s1; + s1 = NULL; + } + else if (c->NumStats != 0) + { + for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++); + if (s->Freq < MAX_FREQ - 9) + { + s->Freq++; + c->SummFreq++; + } + } + else + { + s = ONE_STATE(c); + s->Freq = (Byte)(s->Freq + (!SUFFIX(c)->NumStats & (s->Freq < 24))); + } + successor = SUCCESSOR(s); + if (successor != upBranch) + { + c = CTX(successor); + if (numPs == 0) + return c; + break; + } + ps[numPs++] = s; + } + + upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch); + SetSuccessor(&upState, upBranch + 1); + flags = (Byte)(0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40)); + + if (c->NumStats == 0) + upState.Freq = ONE_STATE(c)->Freq; + else + { + UInt32 cf, s0; + CPpmd_State *s; + for (s = STATS(c); s->Symbol != upState.Symbol; s++); + cf = s->Freq - 1; + s0 = c->SummFreq - c->NumStats - cf; + upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0))); + } + + do + { + /* Create Child */ + CTX_PTR c1; /* = AllocContext(p); */ + if (p->HiUnit != p->LoUnit) + c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); + else if (p->FreeList[0] != 0) + c1 = (CTX_PTR)RemoveNode(p, 0); + else + { + c1 = (CTX_PTR)AllocUnitsRare(p, 0); + if (!c1) + return NULL; + } + c1->NumStats = 0; + c1->Flags = flags; + *ONE_STATE(c1) = upState; + c1->Suffix = REF(c); + SetSuccessor(ps[--numPs], REF(c1)); + c = c1; + } + while (numPs != 0); + + return c; +} + +static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c) +{ + CPpmd_State *s = NULL; + CTX_PTR c1 = c; + CPpmd_Void_Ref upBranch = REF(p->Text); + + #ifdef PPMD8_FREEZE_SUPPORT + /* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */ + CPpmd_State *ps[PPMD8_MAX_ORDER + 1]; + unsigned numPs = 0; + ps[numPs++] = p->FoundState; + #endif + + SetSuccessor(p->FoundState, upBranch); + p->OrderFall++; + + for (;;) + { + if (s1) + { + c = SUFFIX(c); + s = s1; + s1 = NULL; + } + else + { + if (!c->Suffix) + { + #ifdef PPMD8_FREEZE_SUPPORT + if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs); + RESET_TEXT(1); + p->OrderFall = 1; + } + #endif + return c; + } + c = SUFFIX(c); + if (c->NumStats) + { + if ((s = STATS(c))->Symbol != p->FoundState->Symbol) + do { s++; } while (s->Symbol != p->FoundState->Symbol); + if (s->Freq < MAX_FREQ - 9) + { + s->Freq += 2; + c->SummFreq += 2; + } + } + else + { + s = ONE_STATE(c); + s->Freq = (Byte)(s->Freq + (s->Freq < 32)); + } + } + if (SUCCESSOR(s)) + break; + #ifdef PPMD8_FREEZE_SUPPORT + ps[numPs++] = s; + #endif + SetSuccessor(s, upBranch); + p->OrderFall++; + } + + #ifdef PPMD8_FREEZE_SUPPORT + if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + c = CTX(SUCCESSOR(s)); + do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs); + RESET_TEXT(1); + p->OrderFall = 1; + return c; + } + else + #endif + if (SUCCESSOR(s) <= upBranch) + { + CTX_PTR successor; + CPpmd_State *s2 = p->FoundState; + p->FoundState = s; + + successor = CreateSuccessors(p, False, NULL, c); + if (successor == NULL) + SetSuccessor(s, 0); + else + SetSuccessor(s, REF(successor)); + p->FoundState = s2; + } + + if (p->OrderFall == 1 && c1 == p->MaxContext) + { + SetSuccessor(p->FoundState, SUCCESSOR(s)); + p->Text--; + } + if (SUCCESSOR(s) == 0) + return NULL; + return CTX(SUCCESSOR(s)); +} + +static void UpdateModel(CPpmd8 *p) +{ + CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState); + CTX_PTR c; + unsigned s0, ns, fFreq = p->FoundState->Freq; + Byte flag, fSymbol = p->FoundState->Symbol; + CPpmd_State *s = NULL; + + if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0) + { + c = SUFFIX(p->MinContext); + + if (c->NumStats == 0) + { + s = ONE_STATE(c); + if (s->Freq < 32) + s->Freq++; + } + else + { + s = STATS(c); + if (s->Symbol != p->FoundState->Symbol) + { + do { s++; } while (s->Symbol != p->FoundState->Symbol); + if (s[0].Freq >= s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + s--; + } + } + if (s->Freq < MAX_FREQ - 9) + { + s->Freq += 2; + c->SummFreq += 2; + } + } + } + + c = p->MaxContext; + if (p->OrderFall == 0 && fSuccessor) + { + CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext); + if (cs == 0) + { + SetSuccessor(p->FoundState, 0); + RESTORE_MODEL(c, CTX(fSuccessor)); + } + else + { + SetSuccessor(p->FoundState, REF(cs)); + p->MaxContext = cs; + } + return; + } + + *p->Text++ = p->FoundState->Symbol; + successor = REF(p->Text); + if (p->Text >= p->UnitsStart) + { + RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */ + return; + } + + if (!fSuccessor) + { + CTX_PTR cs = ReduceOrder(p, s, p->MinContext); + if (cs == NULL) + { + RESTORE_MODEL(c, 0); + return; + } + fSuccessor = REF(cs); + } + else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart) + { + CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext); + if (cs == NULL) + { + RESTORE_MODEL(c, 0); + return; + } + fSuccessor = REF(cs); + } + + if (--p->OrderFall == 0) + { + successor = fSuccessor; + p->Text -= (p->MaxContext != p->MinContext); + } + #ifdef PPMD8_FREEZE_SUPPORT + else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + successor = fSuccessor; + RESET_TEXT(0); + p->OrderFall = 0; + } + #endif + + s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq; + flag = (Byte)(0x08 * (fSymbol >= 0x40)); + + for (; c != p->MinContext; c = SUFFIX(c)) + { + unsigned ns1; + UInt32 cf, sf; + if ((ns1 = c->NumStats) != 0) + { + if ((ns1 & 1) != 0) + { + /* Expand for one UNIT */ + unsigned oldNU = (ns1 + 1) >> 1; + unsigned i = U2I(oldNU); + if (i != U2I(oldNU + 1)) + { + void *ptr = AllocUnits(p, i + 1); + void *oldPtr; + if (!ptr) + { + RESTORE_MODEL(c, CTX(fSuccessor)); + return; + } + oldPtr = STATS(c); + MyMem12Cpy(ptr, oldPtr, oldNU); + InsertNode(p, oldPtr, i); + c->Stats = STATS_REF(ptr); + } + } + c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns)); + } + else + { + CPpmd_State *s2 = (CPpmd_State*)AllocUnits(p, 0); + if (!s2) + { + RESTORE_MODEL(c, CTX(fSuccessor)); + return; + } + *s2 = *ONE_STATE(c); + c->Stats = REF(s2); + if (s2->Freq < MAX_FREQ / 4 - 1) + s2->Freq <<= 1; + else + s2->Freq = MAX_FREQ - 4; + c->SummFreq = (UInt16)(s2->Freq + p->InitEsc + (ns > 2)); + } + cf = 2 * fFreq * (c->SummFreq + 6); + sf = (UInt32)s0 + c->SummFreq; + if (cf < 6 * sf) + { + cf = 1 + (cf > sf) + (cf >= 4 * sf); + c->SummFreq += 4; + } + else + { + cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf); + c->SummFreq = (UInt16)(c->SummFreq + cf); + } + { + CPpmd_State *s2 = STATS(c) + ns1 + 1; + SetSuccessor(s2, successor); + s2->Symbol = fSymbol; + s2->Freq = (Byte)cf; + c->Flags |= flag; + c->NumStats = (Byte)(ns1 + 1); + } + } + p->MaxContext = p->MinContext = CTX(fSuccessor); +} + +static void Rescale(CPpmd8 *p) +{ + unsigned i, adder, sumFreq, escFreq; + CPpmd_State *stats = STATS(p->MinContext); + CPpmd_State *s = p->FoundState; + { + CPpmd_State tmp = *s; + for (; s != stats; s--) + s[0] = s[-1]; + *s = tmp; + } + escFreq = p->MinContext->SummFreq - s->Freq; + s->Freq += 4; + adder = (p->OrderFall != 0 + #ifdef PPMD8_FREEZE_SUPPORT + || p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE + #endif + ); + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq = s->Freq; + + i = p->MinContext->NumStats; + do + { + escFreq -= (++s)->Freq; + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq += s->Freq; + if (s[0].Freq > s[-1].Freq) + { + CPpmd_State *s1 = s; + CPpmd_State tmp = *s1; + do + s1[0] = s1[-1]; + while (--s1 != stats && tmp.Freq > s1[-1].Freq); + *s1 = tmp; + } + } + while (--i); + + if (s->Freq == 0) + { + unsigned numStats = p->MinContext->NumStats; + unsigned n0, n1; + do { i++; } while ((--s)->Freq == 0); + escFreq += i; + p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i); + if (p->MinContext->NumStats == 0) + { + CPpmd_State tmp = *stats; + tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq); + if (tmp.Freq > MAX_FREQ / 3) + tmp.Freq = MAX_FREQ / 3; + InsertNode(p, stats, U2I((numStats + 2) >> 1)); + p->MinContext->Flags = (Byte)((p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40)); + *(p->FoundState = ONE_STATE(p->MinContext)) = tmp; + return; + } + n0 = (numStats + 2) >> 1; + n1 = (p->MinContext->NumStats + 2) >> 1; + if (n0 != n1) + p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1)); + p->MinContext->Flags &= ~0x08; + p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40); + i = p->MinContext->NumStats; + do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i); + } + p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1)); + p->MinContext->Flags |= 0x4; + p->FoundState = STATS(p->MinContext); +} + +CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq) +{ + CPpmd_See *see; + if (p->MinContext->NumStats != 0xFF) + { + see = p->See[(unsigned)p->NS2Indx[(unsigned)p->MinContext->NumStats + 2] - 3] + + (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) + + 2 * (unsigned)(2 * (unsigned)p->MinContext->NumStats < + ((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) + + p->MinContext->Flags; + { + unsigned r = (see->Summ >> see->Shift); + see->Summ = (UInt16)(see->Summ - r); + *escFreq = r + (r == 0); + } + } + else + { + see = &p->DummySee; + *escFreq = 1; + } + return see; +} + +static void NextContext(CPpmd8 *p) +{ + CTX_PTR c = CTX(SUCCESSOR(p->FoundState)); + if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart) + p->MinContext = p->MaxContext = c; + else + { + UpdateModel(p); + p->MinContext = p->MaxContext; + } +} + +void Ppmd8_Update1(CPpmd8 *p) +{ + CPpmd_State *s = p->FoundState; + s->Freq += 4; + p->MinContext->SummFreq += 4; + if (s[0].Freq > s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + p->FoundState = --s; + if (s->Freq > MAX_FREQ) + Rescale(p); + } + NextContext(p); +} + +void Ppmd8_Update1_0(CPpmd8 *p) +{ + p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq); + p->RunLength += p->PrevSuccess; + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + NextContext(p); +} + +void Ppmd8_UpdateBin(CPpmd8 *p) +{ + p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196)); + p->PrevSuccess = 1; + p->RunLength++; + NextContext(p); +} + +void Ppmd8_Update2(CPpmd8 *p) +{ + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + p->RunLength = p->InitRL; + UpdateModel(p); + p->MinContext = p->MaxContext; +} + +/* Ppmd8Dec.c -- PPMdI Decoder +2010-04-16 : Igor Pavlov : Public domain +This code is based on: + PPMd var.I (2002): Dmitry Shkarin : Public domain + Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ + +Bool Ppmd8_RangeDec_Init(CPpmd8 *p) +{ + unsigned i; + p->Low = 0; + p->Range = 0xFFFFFFFF; + p->Code = 0; + for (i = 0; i < 4; i++) + p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In); + return (p->Code < 0xFFFFFFFF); +} + +static UInt32 RangeDec_GetThreshold(CPpmd8 *p, UInt32 total) +{ + return p->Code / (p->Range /= total); +} + +static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size) +{ + start *= p->Range; + p->Low += start; + p->Code -= start; + p->Range *= size; + + while ((p->Low ^ (p->Low + p->Range)) < kTop || + (p->Range < kBot && ((p->Range = (0 - p->Low) & (kBot - 1)), 1))) + { + p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In); + p->Range <<= 8; + p->Low <<= 8; + } +} + +#define MASK(sym) ((signed char *)charMask)[sym] + +int Ppmd8_DecodeSymbol(CPpmd8 *p) +{ + size_t charMask[256 / sizeof(size_t)]; + if (p->MinContext->NumStats != 0) + { + CPpmd_State *s = Ppmd8_GetStats(p, p->MinContext); + unsigned i; + UInt32 count, hiCnt; + if ((count = RangeDec_GetThreshold(p, p->MinContext->SummFreq)) < (hiCnt = s->Freq)) + { + Byte symbol; + RangeDec_Decode(p, 0, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd8_Update1_0(p); + return symbol; + } + p->PrevSuccess = 0; + i = p->MinContext->NumStats; + do + { + if ((hiCnt += (++s)->Freq) > count) + { + Byte symbol; + RangeDec_Decode(p, hiCnt - s->Freq, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd8_Update1(p); + return symbol; + } + } + while (--i); + if (count >= p->MinContext->SummFreq) + return -2; + RangeDec_Decode(p, hiCnt, p->MinContext->SummFreq - hiCnt); + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(s->Symbol) = 0; + i = p->MinContext->NumStats; + do { MASK((--s)->Symbol) = 0; } while (--i); + } + else + { + UInt16 *prob = Ppmd8_GetBinSumm(p); + if (((p->Code / (p->Range >>= 14)) < *prob)) + { + Byte symbol; + RangeDec_Decode(p, 0, *prob); + *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob); + symbol = (p->FoundState = Ppmd8Context_OneState(p->MinContext))->Symbol; + Ppmd8_UpdateBin(p); + return symbol; + } + RangeDec_Decode(p, *prob, (1 << 14) - *prob); + *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob); + p->InitEsc = PPMD8_kExpEscape[*prob >> 10]; + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(Ppmd8Context_OneState(p->MinContext)->Symbol) = 0; + p->PrevSuccess = 0; + } + for (;;) + { + CPpmd_State *ps[256], *s; + UInt32 freqSum, count, hiCnt; + CPpmd_See *see; + unsigned i, num, numMasked = p->MinContext->NumStats; + do + { + p->OrderFall++; + if (!p->MinContext->Suffix) + return -1; + p->MinContext = Ppmd8_GetContext(p, p->MinContext->Suffix); + } + while (p->MinContext->NumStats == numMasked); + hiCnt = 0; + s = Ppmd8_GetStats(p, p->MinContext); + i = 0; + num = p->MinContext->NumStats - numMasked; + do + { + int k = (int)(MASK(s->Symbol)); + hiCnt += (s->Freq & k); + ps[i] = s++; + i -= k; + } + while (i != num); + + see = Ppmd8_MakeEscFreq(p, numMasked, &freqSum); + freqSum += hiCnt; + count = RangeDec_GetThreshold(p, freqSum); + + if (count < hiCnt) + { + Byte symbol; + CPpmd_State **pps = ps; + for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++); + s = *pps; + RangeDec_Decode(p, hiCnt - s->Freq, s->Freq); + Ppmd_See_Update(see); + p->FoundState = s; + symbol = s->Symbol; + Ppmd8_Update2(p); + return symbol; + } + if (count >= freqSum) + return -2; + RangeDec_Decode(p, hiCnt, freqSum - hiCnt); + see->Summ = (UInt16)(see->Summ + freqSum); + do { MASK(ps[--i]->Symbol) = 0; } while (i != 0); + } +} + +/* H->I changes: + NS2Indx + GlewCount, and Glue method + BinSum + See / EscFreq + CreateSuccessors updates more suffix contexts + UpdateModel consts. + PrevSuccess Update +*/ + +const IPpmd8 __archive_ppmd8_functions = +{ + &Ppmd8_Construct, + &Ppmd8_Alloc, + &Ppmd8_Free, + &Ppmd8_Init, + &Ppmd8_RangeDec_Init, + &Ppmd8_DecodeSymbol, +}; diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd8_private.h b/Utilities/cmlibarchive/libarchive/archive_ppmd8_private.h new file mode 100644 index 00000000000..454b75f41f2 --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd8_private.h @@ -0,0 +1,148 @@ +/* Ppmd8.h -- PPMdI codec +2011-01-27 : Igor Pavlov : Public domain +This code is based on: + PPMd var.I (2002): Dmitry Shkarin : Public domain + Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ + +#ifndef ARCHIVE_PPMD8_PRIVATE_H_INCLUDED +#define ARCHIVE_PPMD8_PRIVATE_H_INCLUDED + +#include "archive_ppmd_private.h" + +#define PPMD8_MIN_ORDER 2 +#define PPMD8_MAX_ORDER 16 + +struct CPpmd8_Context_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd8_Context_ * + #else + UInt32 + #endif + CPpmd8_Context_Ref; + +#pragma pack(push, 1) + +typedef struct CPpmd8_Context_ +{ + Byte NumStats; + Byte Flags; + UInt16 SummFreq; + CPpmd_State_Ref Stats; + CPpmd8_Context_Ref Suffix; +} CPpmd8_Context; + +#pragma pack(pop) + +#define Ppmd8Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq) + +/* The BUG in Shkarin's code for FREEZE mode was fixed, but that fixed + code is not compatible with original code for some files compressed + in FREEZE mode. So we disable FREEZE mode support. */ + +enum +{ + PPMD8_RESTORE_METHOD_RESTART, + PPMD8_RESTORE_METHOD_CUT_OFF + #ifdef PPMD8_FREEZE_SUPPORT + , PPMD8_RESTORE_METHOD_FREEZE + #endif +}; + +typedef struct +{ + CPpmd8_Context *MinContext, *MaxContext; + CPpmd_State *FoundState; + unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder; + Int32 RunLength, InitRL; /* must be 32-bit at least */ + + UInt32 Size; + UInt32 GlueCount; + Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart; + UInt32 AlignOffset; + unsigned RestoreMethod; + + /* Range Coder */ + UInt32 Range; + UInt32 Code; + UInt32 Low; + union + { + IByteIn *In; + IByteOut *Out; + } Stream; + + Byte Indx2Units[PPMD_NUM_INDEXES]; + Byte Units2Indx[128]; + CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES]; + UInt32 Stamps[PPMD_NUM_INDEXES]; + + Byte NS2BSIndx[256], NS2Indx[260]; + CPpmd_See DummySee, See[24][32]; + UInt16 BinSumm[25][64]; +} CPpmd8; + +void Ppmd8_Construct(CPpmd8 *p); +Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size); +void Ppmd8_Free(CPpmd8 *p); +void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod); +#define Ppmd8_WasAllocated(p) ((p)->Base != NULL) + + +/* ---------- Internal Functions ---------- */ + +extern const Byte PPMD8_kExpEscape[16]; + +#ifdef PPMD_32BIT + #define Ppmd8_GetPtr(p, ptr) (ptr) + #define Ppmd8_GetContext(p, ptr) (ptr) + #define Ppmd8_GetStats(p, ctx) ((ctx)->Stats) +#else + #define Ppmd8_GetPtr(p, offs) ((void *)((p)->Base + (offs))) + #define Ppmd8_GetContext(p, offs) ((CPpmd8_Context *)Ppmd8_GetPtr((p), (offs))) + #define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats))) +#endif + +void Ppmd8_Update1(CPpmd8 *p); +void Ppmd8_Update1_0(CPpmd8 *p); +void Ppmd8_Update2(CPpmd8 *p); +void Ppmd8_UpdateBin(CPpmd8 *p); + +#define Ppmd8_GetBinSumm(p) \ + &p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \ + p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \ + p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)] + +CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale); + + +/* ---------- Decode ---------- */ + +Bool Ppmd8_RangeDec_Init(CPpmd8 *p); +#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0) +int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */ + +/* ---------- Encode ---------- */ + +#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; } +void Ppmd8_RangeEnc_FlushData(CPpmd8 *p); +void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */ + +typedef struct +{ + /* Base Functions */ + void (*Ppmd8_Construct)(CPpmd8 *p); + Bool (*Ppmd8_Alloc)(CPpmd8 *p, UInt32 size); + void (*Ppmd8_Free)(CPpmd8 *p); + void (*Ppmd8_Init)(CPpmd8 *p, unsigned max_order, unsigned restore_method); + #define Ppmd7_WasAllocated(p) ((p)->Base != NULL) + + /* Decode Functions */ + int (*Ppmd8_RangeDec_Init)(CPpmd8 *p); + int (*Ppmd8_DecodeSymbol)(CPpmd8 *p); +} IPpmd8; + +extern const IPpmd8 __archive_ppmd8_functions; + +#endif diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd_private.h b/Utilities/cmlibarchive/libarchive/archive_ppmd_private.h index a83b8514d87..582803e5fd0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_ppmd_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd_private.h @@ -2,13 +2,13 @@ 2010-03-12 : Igor Pavlov : Public domain This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ +#ifndef ARCHIVE_PPMD_PRIVATE_H_INCLUDED +#define ARCHIVE_PPMD_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_PPMD_PRIVATE_H_INCLUDED -#define ARCHIVE_PPMD_PRIVATE_H_INCLUDED - #include #include "archive_read_private.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_private.h b/Utilities/cmlibarchive/libarchive/archive_private.h index 4b4be9796df..937a87bb1ef 100644 --- a/Utilities/cmlibarchive/libarchive/archive_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_private.h @@ -25,13 +25,13 @@ * $FreeBSD: head/lib/libarchive/archive_private.h 201098 2009-12-28 02:58:14Z kientzle $ */ +#ifndef ARCHIVE_PRIVATE_H_INCLUDED +#define ARCHIVE_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_PRIVATE_H_INCLUDED -#define ARCHIVE_PRIVATE_H_INCLUDED - #if HAVE_ICONV_H #include #endif @@ -153,6 +153,11 @@ void __archive_errx(int retvalue, const char *msg) __LA_DEAD; void __archive_ensure_cloexec_flag(int fd); int __archive_mktemp(const char *tmpdir); +#if defined(_WIN32) && !defined(__CYGWIN__) +int __archive_mkstemp(wchar_t *template); +#else +int __archive_mkstemp(char *template); +#endif int __archive_clean(struct archive *); diff --git a/Utilities/cmlibarchive/libarchive/archive_random_private.h b/Utilities/cmlibarchive/libarchive/archive_random_private.h index c414779f8d4..08b91b3b7a3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_random_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_random_private.h @@ -23,13 +23,13 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED +#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED -#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED - /* Random number generator. */ int archive_random(void *buf, size_t nbytes); diff --git a/Utilities/cmlibarchive/libarchive/archive_rb.h b/Utilities/cmlibarchive/libarchive/archive_rb.h index 4562e9ebc41..8851f108186 100644 --- a/Utilities/cmlibarchive/libarchive/archive_rb.h +++ b/Utilities/cmlibarchive/libarchive/archive_rb.h @@ -28,8 +28,9 @@ * * Based on NetBSD: rb.h,v 1.13 2009/08/16 10:57:01 yamt Exp */ -#ifndef ARCHIVE_RB_H_ -#define ARCHIVE_RB_H_ + +#ifndef ARCHIVE_RB_H_INCLUDED +#define ARCHIVE_RB_H_INCLUDED struct archive_rb_node { struct archive_rb_node *rb_nodes[2]; @@ -48,12 +49,24 @@ struct archive_rb_node { __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_LEFT) #define ARCHIVE_RB_TREE_MAX(T) \ __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_RIGHT) +#define ARCHIVE_RB_TREE_NEXT(T, N) \ + __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_RIGHT) +#define ARCHIVE_RB_TREE_PREV(T, N) \ + __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_LEFT) #define ARCHIVE_RB_TREE_FOREACH(N, T) \ for ((N) = ARCHIVE_RB_TREE_MIN(T); (N); \ - (N) = __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_RIGHT)) + (N) = ARCHIVE_RB_TREE_NEXT((T), (N))) #define ARCHIVE_RB_TREE_FOREACH_REVERSE(N, T) \ for ((N) = ARCHIVE_RB_TREE_MAX(T); (N); \ - (N) = __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_LEFT)) + (N) = ARCHIVE_RB_TREE_PREV((T), (N))) +#define ARCHIVE_RB_TREE_FOREACH_SAFE(N, T, S) \ + for ((N) = ARCHIVE_RB_TREE_MIN(T); \ + (N) && ((S) = ARCHIVE_RB_TREE_NEXT((T), (N)), 1); \ + (N) = (S)) +#define ARCHIVE_RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \ + for ((N) = ARCHIVE_RB_TREE_MAX(T); \ + (N) && ((S) = ARCHIVE_RB_TREE_PREV((T), (N)), 1); \ + (N) = (S)) /* * archive_rbto_compare_nodes_fn: diff --git a/Utilities/cmlibarchive/libarchive/archive_read.3 b/Utilities/cmlibarchive/libarchive/archive_read.3 index d37e7327cb5..cbedd0a1912 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read.3 @@ -155,7 +155,7 @@ to close the archive, then call .Fn archive_read_free to release all resources, including all memory allocated by the library. .\" -.Sh EXAMPLE +.Sh EXAMPLES The following illustrates basic usage of the library. In this example, the callback functions are simply wrappers around the standard @@ -217,16 +217,16 @@ myclose(struct archive *a, void *client_data) .\" .Sh ERRORS .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , -.Xr archive_read_new 3 , .Xr archive_read_data 3 , .Xr archive_read_extract 3 , .Xr archive_read_filter 3 , .Xr archive_read_format 3 , .Xr archive_read_header 3 , +.Xr archive_read_new 3 , .Xr archive_read_open 3 , .Xr archive_read_set_options 3 , .Xr archive_util 3 , +.Xr libarchive 3 , .Xr tar 5 .Sh HISTORY The diff --git a/Utilities/cmlibarchive/libarchive/archive_read.c b/Utilities/cmlibarchive/libarchive/archive_read.c index 0e56e76e731..4a933b2fc08 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read.c +++ b/Utilities/cmlibarchive/libarchive/archive_read.c @@ -433,7 +433,7 @@ archive_read_add_callback_data(struct archive *_a, void *client_data, return ARCHIVE_FATAL; } a->client.dataset = (struct archive_read_data_node *)p; - for (i = a->client.nodes - 1; i > iindex && i > 0; i--) { + for (i = a->client.nodes - 1; i > iindex; i--) { a->client.dataset[i].data = a->client.dataset[i-1].data; a->client.dataset[i].begin_position = -1; a->client.dataset[i].total_size = -1; @@ -611,6 +611,15 @@ choose_filters(struct archive_read *a) return (ARCHIVE_FATAL); } +int +__archive_read_header(struct archive_read *a, struct archive_entry *entry) +{ + if (a->filter->read_header) + return a->filter->read_header(a->filter, entry); + else + return (ARCHIVE_OK); +} + /* * Read header of next entry. */ @@ -835,7 +844,8 @@ archive_read_data(struct archive *_a, void *buff, size_t s) dest = (char *)buff; while (s > 0) { - if (a->read_data_remaining == 0) { + if (a->read_data_offset == a->read_data_output_offset && + a->read_data_remaining == 0) { read_buf = a->read_data_block; a->read_data_is_posix_read = 1; a->read_data_requested = s; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.3 b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.3 index 8b242ea79b1..ca60d4fc62f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.3 @@ -59,16 +59,16 @@ or empty, this function will do nothing and will be returned. Otherwise, .Cm ARCHIVE_OK -will be returned. +will be returned. .It Fn archive_read_set_passphrase_callback -Register callback function that will be invoked to get a passphrase -for decrption after trying all passphrases registered by the +Register a callback function that will be invoked to get a passphrase +for decryption after trying all the passphrases registered by the .Fn archive_read_add_passphrase function failed. .El .\" .Sh ERRORS .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_read 3 , -.Xr archive_read_set_options 3 +.Xr archive_read_set_options 3 , +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/archive_read_data.3 b/Utilities/cmlibarchive/libarchive/archive_read_data.3 index c1bc15d7cc8..78c0c900041 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_data.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_data.3 @@ -28,7 +28,7 @@ .Dt ARCHIVE_READ_DATA 3 .Os .Sh NAME -.Nm archive_read_data +.Nm archive_read_data , .Nm archive_read_data_block , .Nm archive_read_data_skip , .Nm archive_read_data_into_fd @@ -118,7 +118,6 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_read 3 , .Xr archive_read_extract 3 , .Xr archive_read_filter 3 , @@ -127,4 +126,5 @@ functions. .Xr archive_read_open 3 , .Xr archive_read_set_options 3 , .Xr archive_util 3 , +.Xr libarchive 3 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk.3 b/Utilities/cmlibarchive/libarchive/archive_read_disk.3 index 027f63cb630..82d6a5c8562 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk.3 @@ -99,9 +99,10 @@ following values: .Bl -tag -compact -width "indent" .It Cm ARCHIVE_READDISK_HONOR_NODUMP Skip files and directories with the nodump file attribute (file flag) set. -By default, the nodump file atrribute is ignored. +By default, the nodump file attribute is ignored. .It Cm ARCHIVE_READDISK_MAC_COPYFILE -Mac OS X specific. Read metadata (ACLs and extended attributes) with +Mac OS X specific. +Read metadata (ACLs and extended attributes) with .Xr copyfile 3 . By default, metadata is read using .Xr copyfile 3 . @@ -120,7 +121,7 @@ or for more information on file attributes. .It Cm ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS Do not traverse mount points. -By defaut, moint points are traversed. +By default, mount points are traversed. .It Cm ARCHIVE_READDISK_NO_XATTR Do not read extended file attributes (xattrs). By default, extended file attributes are read from disk. @@ -216,7 +217,7 @@ of some other operation. (For example, directory traversal libraries often provide this information.) .Pp Where necessary, user and group ids are converted to user and group names -using the currently registered lookup functions above. +using the currently-registered lookup functions above. This affects the file ownership fields and ACL values in the .Tn struct archive_entry object. @@ -226,7 +227,7 @@ More information about the object and the overall design of the library can be found in the .Xr libarchive 3 overview. -.Sh EXAMPLE +.Sh EXAMPLES The following illustrates basic usage of the library by showing how to use it to copy an item on disk into an archive. .Bd -literal -offset indent @@ -291,11 +292,11 @@ and functions. .\" .Sh SEE ALSO +.Xr tar 1 , .Xr archive_read 3 , .Xr archive_util 3 , .Xr archive_write 3 , .Xr archive_write_disk 3 , -.Xr tar 1 , .Xr libarchive 3 .Sh HISTORY The diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c index 1786cff3845..2a8cec8d117 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c @@ -163,6 +163,9 @@ archive_read_disk_entry_from_file(struct archive *_a, int initial_fd = fd; int r, r1; + archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, + "archive_read_disk_entry_from_file"); + archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) @@ -188,7 +191,7 @@ archive_read_disk_entry_from_file(struct archive *_a, } } else #endif - if (stat(path, &s) != 0) { + if (la_stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); @@ -246,11 +249,11 @@ archive_read_disk_entry_from_file(struct archive *_a, #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) if (S_ISLNK(st->st_mode)) { - size_t linkbuffer_len = st->st_size + 1; + size_t linkbuffer_len = st->st_size; char *linkbuffer; int lnklen; - linkbuffer = malloc(linkbuffer_len); + linkbuffer = malloc(linkbuffer_len + 1); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't read link data"); @@ -277,7 +280,7 @@ archive_read_disk_entry_from_file(struct archive *_a, free(linkbuffer); return (ARCHIVE_FAILED); } - linkbuffer[lnklen] = 0; + linkbuffer[lnklen] = '\0'; archive_entry_set_symlink(entry, linkbuffer); free(linkbuffer); } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c index cdf7541238c..52fec7bb42c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c @@ -694,6 +694,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, struct tree *t = a->tree; int r; ssize_t bytes; + int64_t sparse_bytes; size_t buffbytes; int empty_sparse_region = 0; @@ -728,27 +729,23 @@ _archive_read_data_block(struct archive *_a, const void **buff, if ((t->flags & needsRestoreTimes) != 0 && t->restore_time.noatime == 0) flags |= O_NOATIME; - do { #endif - t->entry_fd = open_on_current_dir(t, - tree_current_access_path(t), flags); - __archive_ensure_cloexec_flag(t->entry_fd); + t->entry_fd = open_on_current_dir(t, + tree_current_access_path(t), flags); + __archive_ensure_cloexec_flag(t->entry_fd); #if defined(O_NOATIME) - /* - * When we did open the file with O_NOATIME flag, - * if successful, set 1 to t->restore_time.noatime - * not to restore an atime of the file later. - * if failed by EPERM, retry it without O_NOATIME flag. - */ - if (flags & O_NOATIME) { - if (t->entry_fd >= 0) - t->restore_time.noatime = 1; - else if (errno == EPERM) { - flags &= ~O_NOATIME; - continue; - } - } - } while (0); + /* + * When we did open the file with O_NOATIME flag, + * if successful, set 1 to t->restore_time.noatime + * not to restore an atime of the file later. + * if failed by EPERM, retry it without O_NOATIME flag. + */ + if (flags & O_NOATIME) { + if (t->entry_fd >= 0) + t->restore_time.noatime = 1; + else if (errno == EPERM) + flags &= ~O_NOATIME; + } #endif if (t->entry_fd < 0) { archive_set_error(&a->archive, errno, @@ -792,9 +789,9 @@ _archive_read_data_block(struct archive *_a, const void **buff, a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } - bytes = t->current_sparse->offset - t->entry_total; - t->entry_remaining_bytes -= bytes; - t->entry_total += bytes; + sparse_bytes = t->current_sparse->offset - t->entry_total; + t->entry_remaining_bytes -= sparse_bytes; + t->entry_total += sparse_bytes; } /* @@ -856,7 +853,12 @@ next_entry(struct archive_read_disk *a, struct tree *t, const struct stat *st; /* info to use for this entry */ const struct stat *lst;/* lstat() information */ const char *name; - int descend, r; + int delayed, delayed_errno, descend, r; + struct archive_string delayed_str; + + delayed = ARCHIVE_OK; + delayed_errno = 0; + archive_string_init(&delayed_str); st = NULL; lst = NULL; @@ -885,14 +887,26 @@ next_entry(struct archive_read_disk *a, struct tree *t, case TREE_REGULAR: lst = tree_current_lstat(t); if (lst == NULL) { + if (errno == ENOENT && t->depth > 0) { + delayed = ARCHIVE_WARN; + delayed_errno = errno; + if (delayed_str.length == 0) { + archive_string_sprintf(&delayed_str, + "%s", tree_current_path(t)); + } else { + archive_string_sprintf(&delayed_str, + " %s", tree_current_path(t)); + } + } else { archive_set_error(&a->archive, errno, "%s: Cannot stat", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); + } } break; - } + } } while (lst == NULL); #ifdef __APPLE__ @@ -1083,6 +1097,17 @@ next_entry(struct archive_read_disk *a, struct tree *t, r = archive_read_disk_entry_from_file(&(a->archive), entry, t->entry_fd, st); + if (r == ARCHIVE_OK) { + r = delayed; + if (r != ARCHIVE_OK) { + archive_string_sprintf(&delayed_str, ": %s", + "File removed before we read it"); + archive_set_error(&(a->archive), delayed_errno, + "%s", delayed_str.s); + } + } + archive_string_free(&delayed_str); + return (r); } @@ -1114,6 +1139,8 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) t->entry_fd = -1; } + archive_entry_clear(entry); + for (;;) { r = next_entry(a, t, entry); if (t->entry_fd >= 0) { @@ -1266,10 +1293,23 @@ archive_read_disk_descend(struct archive *_a) if (t->visit_type != TREE_REGULAR || !t->descend) return (ARCHIVE_OK); + /* + * We must not treat the initial specified path as a physical dir, + * because if we do then we will try and ascend out of it by opening + * ".." which is (a) wrong and (b) causes spurious permissions errors + * if ".." is not readable by us. Instead, treat it as if it were a + * symlink. (This uses an extra fd, but it can only happen once at the + * top level of a traverse.) But we can't necessarily assume t->st is + * valid here (though t->lst is), which complicates the logic a + * little. + */ if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->lst.st_dev, t->lst.st_ino, &t->restore_time); - t->stack->flags |= isDir; + if (t->stack->parent->parent != NULL) + t->stack->flags |= isDir; + else + t->stack->flags |= isDirLink; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->st.st_dev, t->st.st_ino, &t->restore_time); @@ -2122,6 +2162,17 @@ tree_open(const char *path, int symlink_mode, int restore_time) static struct tree * tree_reopen(struct tree *t, const char *path, int restore_time) { +#if defined(O_PATH) + /* Linux */ + const int o_flag = O_PATH; +#elif defined(O_SEARCH) + /* SunOS */ + const int o_flag = O_SEARCH; +#elif defined(__FreeBSD__) && defined(O_EXEC) + /* FreeBSD */ + const int o_flag = O_EXEC; +#endif + t->flags = (restore_time != 0)?needsRestoreTimes:0; t->flags |= onInitialDir; t->visit_type = 0; @@ -2143,6 +2194,16 @@ tree_reopen(struct tree *t, const char *path, int restore_time) t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); +#if defined(O_PATH) || defined(O_SEARCH) || \ + (defined(__FreeBSD__) && defined(O_EXEC)) + /* + * Most likely reason to fail opening "." is that it's not readable, + * so try again for execute. The consequences of not opening this are + * unhelpful and unnecessary errors later. + */ + if (t->initial_dir_fd < 0) + t->initial_dir_fd = open(".", o_flag | O_CLOEXEC); +#endif __archive_ensure_cloexec_flag(t->initial_dir_fd); t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); @@ -2450,7 +2511,7 @@ tree_current_stat(struct tree *t) #else if (tree_enter_working_dir(t) != 0) return NULL; - if (stat(tree_current_access_path(t), &t->st) != 0) + if (la_stat(tree_current_access_path(t), &t->st) != 0) #endif return NULL; t->flags |= hasStat; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_private.h b/Utilities/cmlibarchive/libarchive/archive_read_disk_private.h index f03a0a9cc30..bc8abc15d15 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_private.h @@ -26,13 +26,13 @@ * $FreeBSD: head/lib/libarchive/archive_read_disk_private.h 201105 2009-12-28 03:20:54Z kientzle $ */ +#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED +#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED -#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED - #include "archive_platform_acl.h" struct tree; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c index d82048de299..fdd376f9b94 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c @@ -299,8 +299,155 @@ static int close_and_restore_time(HANDLE, struct tree *, struct restore_time *); static int setup_sparse_from_disk(struct archive_read_disk *, struct archive_entry *, HANDLE); +static int la_linkname_from_handle(HANDLE, wchar_t **, int *); +static int la_linkname_from_pathw(const wchar_t *, wchar_t **, int *); +static void entry_symlink_from_pathw(struct archive_entry *, + const wchar_t *path); + +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; +/* + * Reads the target of a symbolic link + * + * Returns 0 on success and -1 on failure + * outbuf is allocated in the function + */ +static int +la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype) +{ + DWORD inbytes; + REPARSE_DATA_BUFFER *buf; + BY_HANDLE_FILE_INFORMATION st; + size_t len; + BOOL ret; + BYTE *indata; + wchar_t *tbuf; + + ret = GetFileInformationByHandle(h, &st); + if (ret == 0 || + (st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { + return (-1); + } + + indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata, + 1024, &inbytes, NULL); + if (ret == 0) { + la_dosmaperr(GetLastError()); + free(indata); + return (-1); + } + + buf = (REPARSE_DATA_BUFFER *) indata; + if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) { + free(indata); + /* File is not a symbolic link */ + errno = EINVAL; + return (-1); + } + + len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength; + if (len <= 0) { + free(indata); + return (-1); + } + + tbuf = malloc(len + 1 * sizeof(wchar_t)); + if (tbuf == NULL) { + free(indata); + return (-1); + } + + memcpy(tbuf, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer) + [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len); + free(indata); + + tbuf[len / sizeof(wchar_t)] = L'\0'; + + *linkname = tbuf; + + /* + * Translate backslashes to slashes for libarchive internal use + */ + while(*tbuf != L'\0') { + if (*tbuf == L'\\') + *tbuf = L'/'; + tbuf++; + } + + if ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + *linktype = AE_SYMLINK_TYPE_FILE; + else + *linktype = AE_SYMLINK_TYPE_DIRECTORY; + + return (0); +} +/* + * Returns AE_SYMLINK_TYPE_FILE, AE_SYMLINK_TYPE_DIRECTORY or -1 on error + */ +static int +la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype) +{ + HANDLE h; + const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OPEN_REPARSE_POINT; + int ret; + + h = CreateFileW(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, + NULL); + if (h == INVALID_HANDLE_VALUE) { + la_dosmaperr(GetLastError()); + return (-1); + } + + ret = la_linkname_from_handle(h, outbuf, linktype); + CloseHandle(h); + + return (ret); +} + +static void +entry_symlink_from_pathw(struct archive_entry *entry, const wchar_t *path) +{ + wchar_t *linkname = NULL; + int ret, linktype; + + ret = la_linkname_from_pathw(path, &linkname, &linktype); + if (ret != 0) + return; + if (linktype >= 0) { + archive_entry_copy_symlink_w(entry, linkname); + archive_entry_set_symlink_type(entry, linktype); + } + free(linkname); + + return; +} static struct archive_vtable * archive_read_disk_vtable(void) @@ -899,6 +1046,19 @@ next_entry(struct archive_read_disk *a, struct tree *t, } } + /* + * File attributes + */ + if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) { + const int supported_attrs = + FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM; + DWORD file_attrs = st->dwFileAttributes & supported_attrs; + if (file_attrs != 0) + archive_entry_set_fflags(entry, file_attrs, 0); + } + /* * Invoke a meta data filter callback. */ @@ -966,6 +1126,8 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) t->entry_fh = INVALID_HANDLE_VALUE; } + archive_entry_clear(entry); + while ((r = next_entry(a, t, entry)) == ARCHIVE_RETRY) archive_entry_clear(entry); @@ -1838,9 +2000,10 @@ entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path, mode |= S_IWUSR | S_IWGRP | S_IWOTH; if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && findData != NULL && - findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) + findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) { mode |= S_IFLNK; - else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + entry_symlink_from_pathw(entry, path); + } else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; else { const wchar_t *p; @@ -2139,6 +2302,8 @@ archive_read_disk_entry_from_file(struct archive *_a, fileAttributes = bhfi.dwFileAttributes; } else { archive_entry_copy_stat(entry, st); + if (st->st_mode & S_IFLNK) + entry_symlink_from_pathw(entry, path); h = INVALID_HANDLE_VALUE; } @@ -2150,6 +2315,19 @@ archive_read_disk_entry_from_file(struct archive *_a, if (name != NULL) archive_entry_copy_gname(entry, name); + /* + * File attributes + */ + if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) { + const int supported_attrs = + FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM; + DWORD file_attrs = fileAttributes & supported_attrs; + if (file_attrs != 0) + archive_entry_set_fflags(entry, file_attrs, 0); + } + /* * Can this file be sparse file ? */ diff --git a/Utilities/cmlibarchive/libarchive/archive_read_extract.3 b/Utilities/cmlibarchive/libarchive/archive_read_extract.3 index 6ec0ced939b..858f3974255 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_extract.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_extract.3 @@ -126,7 +126,6 @@ and functions. .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_read 3 , .Xr archive_read_data 3 , .Xr archive_read_filter 3 , @@ -134,4 +133,5 @@ functions. .Xr archive_read_open 3 , .Xr archive_read_set_options 3 , .Xr archive_util 3 , +.Xr libarchive 3 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_read_filter.3 b/Utilities/cmlibarchive/libarchive/archive_read_filter.3 index ef0a7017533..1ba5fcbd6ef 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_filter.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_filter.3 @@ -147,8 +147,8 @@ and functions. .\" .Sh SEE ALSO -.Xr libarchive 3 , .Xr archive_read 3 , .Xr archive_read_data 3 , .Xr archive_read_format 3 , -.Xr archive_read_format 3 +.Xr archive_read_format 3 , +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/archive_read_format.3 b/Utilities/cmlibarchive/libarchive/archive_read_format.3 index 91c5d2cfd4b..f3804ce3796 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_format.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_format.3 @@ -102,7 +102,7 @@ For example, .Fn archive_read_support_format_tar enables support for a variety of standard tar formats, old-style tar, ustar, pax interchange format, and many common variants. -.It Fn archive_read_support_format_all +.It Fn archive_read_support_format_all Enables support for all available formats except the .Dq raw format (see below). @@ -125,7 +125,7 @@ it is not possible to accurately determine a format for an empty file based purely on contents. So empty files are treated by libarchive as a distinct format. -.It Fn archive_read_support_format_raw +.It Fn archive_read_support_format_raw The .Dq raw format handler allows libarchive to be used to read arbitrary data. @@ -153,11 +153,11 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_read_data 3 , .Xr archive_read_filter 3 , .Xr archive_read_set_options 3 , .Xr archive_util 3 , +.Xr libarchive 3 , .Xr tar 5 .Sh BUGS Many traditional archiver programs treat diff --git a/Utilities/cmlibarchive/libarchive/archive_read_free.3 b/Utilities/cmlibarchive/libarchive/archive_read_free.3 index 5b218225ba0..8371c3a0c60 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_free.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_free.3 @@ -83,11 +83,11 @@ and functions. .\" .Sh SEE ALSO -.Xr libarchive 3 , -.Xr archive_read_new 3 , .Xr archive_read_data 3 , .Xr archive_read_filter 3 , .Xr archive_read_format 3 , +.Xr archive_read_new 3 , .Xr archive_read_open 3 , .Xr archive_read_set_options 3 , -.Xr archive_util 3 +.Xr archive_util 3 , +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/archive_read_header.3 b/Utilities/cmlibarchive/libarchive/archive_read_header.3 index 480a666ca39..1e97f3a2750 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_header.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_header.3 @@ -79,7 +79,6 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_read 3 , .Xr archive_read_data 3 , .Xr archive_read_extract 3 , @@ -88,4 +87,5 @@ functions. .Xr archive_read_open 3 , .Xr archive_read_set_options 3 , .Xr archive_util 3 , +.Xr libarchive 3 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_read_new.3 b/Utilities/cmlibarchive/libarchive/archive_read_new.3 index 0c9d1a7fbb2..8bb6b848b06 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_new.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_new.3 @@ -50,10 +50,10 @@ object can be found in the overview manual page for .\" .Sh ERRORS .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_read_data 3 , .Xr archive_read_filter 3 , .Xr archive_read_format 3 , .Xr archive_read_set_options 3 , .Xr archive_util 3 , +.Xr libarchive 3 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open.3 b/Utilities/cmlibarchive/libarchive/archive_read_open.3 index 2278ebc330c..f67677823bd 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_open.3 @@ -205,7 +205,7 @@ On failure, the callback should invoke .Fn archive_set_error to register an error code and message and return -.Cm ARCHIVE_FATAL. +.Cm ARCHIVE_FATAL . .\" .Sh EXAMPLE .\" .Sh RETURN VALUES @@ -223,11 +223,11 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_read 3 , .Xr archive_read_data 3 , .Xr archive_read_filter 3 , .Xr archive_read_format 3 , .Xr archive_read_set_options 3 , .Xr archive_util 3 , +.Xr libarchive 3 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_file.c b/Utilities/cmlibarchive/libarchive/archive_read_open_file.c index bfe933bf32e..101dae6cd9e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open_file.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_open_file.c @@ -174,8 +174,7 @@ file_close(struct archive *a, void *client_data) struct read_FILE_data *mine = (struct read_FILE_data *)client_data; (void)a; /* UNUSED */ - if (mine->buffer != NULL) - free(mine->buffer); + free(mine->buffer); free(mine); return (ARCHIVE_OK); } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_private.h b/Utilities/cmlibarchive/libarchive/archive_read_private.h index 78546dca34a..c842e6f09ad 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_read_private.h @@ -25,15 +25,15 @@ * $FreeBSD: head/lib/libarchive/archive_read_private.h 201088 2009-12-28 02:18:55Z kientzle $ */ +#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED +#define ARCHIVE_READ_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif #endif -#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED -#define ARCHIVE_READ_PRIVATE_H_INCLUDED - #include "archive.h" #include "archive_string.h" #include "archive_private.h" @@ -98,6 +98,8 @@ struct archive_read_filter { int (*close)(struct archive_read_filter *self); /* Function that handles switching from reading one block to the next/prev */ int (*sswitch)(struct archive_read_filter *self, unsigned int iindex); + /* Read any header metadata if available. */ + int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry); /* My private data. */ void *data; @@ -250,6 +252,7 @@ int64_t __archive_read_seek(struct archive_read*, int64_t, int); int64_t __archive_read_filter_seek(struct archive_read_filter *, int64_t, int); int64_t __archive_read_consume(struct archive_read *, int64_t); int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t); +int __archive_read_header(struct archive_read *, struct archive_entry *); int __archive_read_program(struct archive_read_filter *, const char *); void __archive_read_free_filters(struct archive_read *); struct archive_read_extract *__archive_read_get_extract(struct archive_read *); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_set_format.c b/Utilities/cmlibarchive/libarchive/archive_read_set_format.c index 190f4369d24..1d3e49d1647 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_set_format.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_set_format.c @@ -73,6 +73,9 @@ archive_read_set_format(struct archive *_a, int code) case ARCHIVE_FORMAT_RAR: strcpy(str, "rar"); break; + case ARCHIVE_FORMAT_RAR_V5: + strcpy(str, "rar5"); + break; case ARCHIVE_FORMAT_TAR: strcpy(str, "tar"); break; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 b/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 index 1a251cefecd..78d99999cf8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 2, 2012 +.Dd January 31, 2020 .Dt ARCHIVE_READ_OPTIONS 3 .Os .Sh NAME @@ -180,6 +180,18 @@ only to modules whose name matches .\" .Sh OPTIONS .Bl -tag -compact -width indent +.It Format cab +.Bl -tag -compact -width indent +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file names. +.El +.It Format cpio +.Bl -tag -compact -width indent +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file names. +.El .It Format iso9660 .Bl -tag -compact -width indent .It Cm joliet @@ -193,6 +205,24 @@ Defaults to enabled, use .Cm !rockridge to disable. .El +.It Format lha +.Bl -tag -compact -width indent +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file names. +.El +.It Format mtree +.Bl -tag -compact -width indent +.It Cm checkfs +Allow reading information missing from the mtree from the file system. +Disabled by default. +.El +.It Format rar +.Bl -tag -compact -width indent +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file names. +.El .It Format tar .Bl -tag -compact -width indent .It Cm compat-2x @@ -202,7 +232,7 @@ This option mimics the libarchive 2.x filename handling so that such archives can be read correctly. .It Cm hdrcharset The value is used as a character set name that will be -used when translating filenames. +used when translating file names. .It Cm mac-ext Support Mac OS metadata extension that records data in special files beginning with a period and underscore. @@ -212,7 +242,8 @@ Use to disable. .It Cm read_concatenated_archives Ignore zeroed blocks in the archive, which occurs when multiple tar archives -have been concatenated together. Without this option, only the contents of +have been concatenated together. +Without this option, only the contents of the first concatenated archive would be read. .El .El @@ -226,6 +257,6 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , +.Xr archive_read 3 , .Xr archive_write_set_options 3 , -.Xr archive_read 3 +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c index 2b1a5e2aa77..5333d47000c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c @@ -41,7 +41,7 @@ __FBSDID("$FreeBSD$"); #include #endif #ifdef HAVE_BZLIB_H -#include +#include #endif #include "archive.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_gzip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_gzip.c index e9f59f13fd7..ac0b6947595 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_gzip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_gzip.c @@ -37,14 +37,19 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_STRING_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" +#include "archive_entry.h" +#include "archive_endian.h" #include "archive_private.h" #include "archive_read_private.h" @@ -56,6 +61,8 @@ struct private_data { size_t out_block_size; int64_t total_out; unsigned long crc; + uint32_t mtime; + char *name; char eof; /* True = found end of compressed data. */ }; @@ -123,12 +130,21 @@ archive_read_support_filter_gzip(struct archive *_a) * count of bits verified, suitable for use by bidder. */ static ssize_t -peek_at_header(struct archive_read_filter *filter, int *pbits) +peek_at_header(struct archive_read_filter *filter, int *pbits, +#ifdef HAVE_ZLIB_H + struct private_data *state +#else + void *state +#endif + ) { const unsigned char *p; ssize_t avail, len; int bits = 0; int header_flags; +#ifndef HAVE_ZLIB_H + (void)state; /* UNUSED */ +#endif /* Start by looking at the first ten bytes of the header, which * is all fixed layout. */ @@ -144,7 +160,11 @@ peek_at_header(struct archive_read_filter *filter, int *pbits) return (0); bits += 3; header_flags = p[3]; - /* Bytes 4-7 are mod time. */ + /* Bytes 4-7 are mod time in little endian. */ +#ifdef HAVE_ZLIB_H + if (state) + state->mtime = archive_le32dec(p + 4); +#endif /* Byte 8 is deflate flags. */ /* XXXX TODO: return deflate flags back to consume_header for use in initializing the decompressor. */ @@ -161,6 +181,9 @@ peek_at_header(struct archive_read_filter *filter, int *pbits) /* Null-terminated optional filename. */ if (header_flags & 8) { +#ifdef HAVE_ZLIB_H + ssize_t file_start = len; +#endif do { ++len; if (avail < len) @@ -169,6 +192,14 @@ peek_at_header(struct archive_read_filter *filter, int *pbits) if (p == NULL) return (0); } while (p[len - 1] != 0); + +#ifdef HAVE_ZLIB_H + if (state) { + /* Reset the name in case of repeat header reads. */ + free(state->name); + state->name = strdup((const char *)&p[file_start]); + } +#endif } /* Null-terminated optional comment. */ @@ -214,12 +245,11 @@ gzip_bidder_bid(struct archive_read_filter_bidder *self, (void)self; /* UNUSED */ - if (peek_at_header(filter, &bits_checked)) + if (peek_at_header(filter, &bits_checked, NULL)) return (bits_checked); return (0); } - #ifndef HAVE_ZLIB_H /* @@ -243,6 +273,24 @@ gzip_bidder_init(struct archive_read_filter *self) #else +static int +gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry) +{ + struct private_data *state; + + state = (struct private_data *)self->data; + + /* A mtime of 0 is considered invalid/missing. */ + if (state->mtime != 0) + archive_entry_set_mtime(entry, state->mtime, 0); + + /* If the name is available, extract it. */ + if (state->name) + archive_entry_set_pathname(entry, state->name); + + return (ARCHIVE_OK); +} + /* * Initialize the filter object. */ @@ -272,6 +320,9 @@ gzip_bidder_init(struct archive_read_filter *self) self->read = gzip_filter_read; self->skip = NULL; /* not supported */ self->close = gzip_filter_close; +#ifdef HAVE_ZLIB_H + self->read_header = gzip_read_header; +#endif state->in_stream = 0; /* We're not actually within a stream yet. */ @@ -289,7 +340,7 @@ consume_header(struct archive_read_filter *self) state = (struct private_data *)self->data; /* If this is a real header, consume it. */ - len = peek_at_header(self->upstream, NULL); + len = peek_at_header(self->upstream, NULL, state); if (len == 0) return (ARCHIVE_EOF); __archive_read_filter_consume(self->upstream, len); @@ -374,7 +425,7 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; size_t decompressed; - ssize_t avail_in; + ssize_t avail_in, max_in; int ret; state = (struct private_data *)self->data; @@ -408,6 +459,12 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) "truncated gzip input"); return (ARCHIVE_FATAL); } + if (UINT_MAX >= SSIZE_MAX) + max_in = SSIZE_MAX; + else + max_in = UINT_MAX; + if (avail_in > max_in) + avail_in = max_in; state->stream.avail_in = (uInt)avail_in; /* Decompress and consume some of that data. */ @@ -469,6 +526,7 @@ gzip_filter_close(struct archive_read_filter *self) } } + free(state->name); free(state->out_block); free(state); return (ret); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c index 147f5027ff4..43ee6c2b726 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c @@ -460,7 +460,7 @@ lz4_filter_read_descriptor(struct archive_read_filter *self) __archive_read_filter_consume(self->upstream, descriptor_bytes); - /* Make sure we have an enough buffer for uncompressed data. */ + /* Make sure we have a large enough buffer for uncompressed data. */ if (lz4_allocate_out_block(self) != ARCHIVE_OK) return (ARCHIVE_FATAL); if (state->flags.stream_checksum) @@ -520,7 +520,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) if (read_buf == NULL) goto truncated_error; - /* Optional process, checking a block sum. */ + /* Optional processing, checking a block sum. */ if (checksum_size) { unsigned int chsum = __archive_xxhash.XXH32( read_buf + 4, (int)compressed_size, 0); @@ -640,7 +640,7 @@ lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p) if (ret == 0 && *p == NULL) state->stage = SELECT_STREAM; - /* Optional process, checking a stream sum. */ + /* Optional processing, checking a stream sum. */ if (state->flags.stream_checksum) { if (state->stage == SELECT_STREAM) { unsigned int checksum; @@ -660,7 +660,7 @@ lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p) if (checksum != checksum_stream) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, - "lz4 stream cheksum error"); + "lz4 stream checksum error"); return (ARCHIVE_FATAL); } } else if (ret > 0) @@ -674,7 +674,7 @@ static ssize_t lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p) { struct private_data *state = (struct private_data *)self->data; - int compressed; + uint32_t compressed; const char *read_buf; ssize_t ret; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c index 4356b82fdfb..05f740ba3e6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c @@ -50,7 +50,7 @@ __FBSDID("$FreeBSD$"); #include #endif #ifdef HAVE_ZLIB_H -#include /* for crc32 and adler32 */ +#include /* for crc32 and adler32 */ #endif #include "archive.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c index 641297990d2..67ddffb0694 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c @@ -574,14 +574,13 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff) while (l > 0) { int n = 0; - if (l > 0) { - if (!uuchar[b[0]] || !uuchar[b[1]]) - break; - n = UUDECODE(*b++) << 18; - n |= UUDECODE(*b++) << 12; - *out++ = n >> 16; total++; - --l; - } + if (!uuchar[b[0]] || !uuchar[b[1]]) + break; + n = UUDECODE(*b++) << 18; + n |= UUDECODE(*b++) << 12; + *out++ = n >> 16; total++; + --l; + if (l > 0) { if (!uuchar[b[0]]) break; @@ -626,14 +625,13 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff) while (l > 0) { int n = 0; - if (l > 0) { - if (!base64[b[0]] || !base64[b[1]]) - break; - n = base64num[*b++] << 18; - n |= base64num[*b++] << 12; - *out++ = n >> 16; total++; - l -= 2; - } + if (!base64[b[0]] || !base64[b[1]]) + break; + n = base64num[*b++] << 18; + n |= base64num[*b++] << 12; + *out++ = n >> 16; total++; + l -= 2; + if (l > 0) { if (*b == '=') break; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c index a18818638dd..3223b38e560 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c @@ -42,7 +42,7 @@ __FBSDID("$FreeBSD$"); #include #endif #if HAVE_LZMA_H -#include +#include #endif #include "archive.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c index 451370693d3..e1ec60e4e1d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c @@ -45,7 +45,7 @@ __FBSDID("$FreeBSD$"); #include #endif #if HAVE_ZSTD_H -#include +#include #endif #include "archive.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c index a885a4c4520..456b2f8cdce 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c @@ -33,13 +33,13 @@ __FBSDID("$FreeBSD$"); #include #endif #ifdef HAVE_BZLIB_H -#include +#include #endif #ifdef HAVE_LZMA_H -#include +#include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" @@ -1086,10 +1086,17 @@ init_decompression(struct archive_read *a, struct _7zip *zip, zip->bcj_state = 0; break; case _7Z_DELTA: + if (coder2->propertiesSize != 1) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Invalid Delta parameter"); + return (ARCHIVE_FAILED); + } filters[fi].id = LZMA_FILTER_DELTA; memset(&delta_opt, 0, sizeof(delta_opt)); delta_opt.type = LZMA_DELTA_TYPE_BYTE; - delta_opt.dist = 1; + delta_opt.dist = + (uint32_t)coder2->properties[0] + 1; filters[fi].options = &delta_opt; fi++; break; @@ -1787,7 +1794,7 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) return (0); } - if (*p != kSize) + if (*p != kCRC) return (-1); if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0) @@ -2964,13 +2971,7 @@ get_uncompressed_data(struct archive_read *a, const void **buff, size_t size, if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { /* Copy mode. */ - /* - * Note: '1' here is a performance optimization. - * Recall that the decompression layer returns a count of - * available bytes; asking for more than that forces the - * decompressor to combine reads by copying data. - */ - *buff = __archive_read_ahead(a, 1, &bytes_avail); + *buff = __archive_read_ahead(a, minimum, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -3323,8 +3324,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, * Release the memory which the previous folder used for BCJ2. */ for (i = 0; i < 3; i++) { - if (zip->sub_stream_buff[i] != NULL) - free(zip->sub_stream_buff[i]); + free(zip->sub_stream_buff[i]); zip->sub_stream_buff[i] = NULL; } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_all.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_all.c index 2127ebd3353..dea558bbfcc 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_all.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_all.c @@ -72,6 +72,7 @@ archive_read_support_format_all(struct archive *a) archive_read_support_format_7zip(a); archive_read_support_format_cab(a); archive_read_support_format_rar(a); + archive_read_support_format_rar5(a); archive_read_support_format_iso9660(a); /* Seek is really bad, since it forces the read-ahead * logic to discard buffered data. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c index b6b9fc3c6dc..296b7db0411 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c @@ -138,8 +138,7 @@ archive_read_format_ar_cleanup(struct archive_read *a) struct ar *ar; ar = (struct ar *)(a->format->data); - if (ar->strtab) - free(ar->strtab); + free(ar->strtab); free(ar); (a->format->data) = NULL; return (ARCHIVE_OK); @@ -388,9 +387,10 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, /* * "/" is the SVR4/GNU archive symbol table. + * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table. */ - if (strcmp(filename, "/") == 0) { - archive_entry_copy_pathname(entry, "/"); + if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) { + archive_entry_copy_pathname(entry, filename); /* Parse the time, owner, mode, size fields. */ r = ar_parse_common_header(ar, entry, h); /* Force the file type to a regular file. */ @@ -459,6 +459,7 @@ ar_parse_common_header(struct ar *ar, struct archive_entry *entry, uint64_t n; /* Copy remaining header */ + archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_mtime(entry, (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L); archive_entry_set_uid(entry, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c index 084563f4310..034353d78f6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c @@ -60,6 +60,9 @@ archive_read_support_format_by_code(struct archive *a, int format_code) case ARCHIVE_FORMAT_RAR: return archive_read_support_format_rar(a); break; + case ARCHIVE_FORMAT_RAR_V5: + return archive_read_support_format_rar5(a); + break; case ARCHIVE_FORMAT_TAR: return archive_read_support_format_tar(a); break; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c index 51f79fa6bbb..58644baf2d0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c @@ -38,7 +38,7 @@ #include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" @@ -1509,8 +1509,8 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) } if (mszip == 1 && cab->stream.next_in[0] != 0x4b) goto nomszip; - else if (cab->stream.next_in[0] != 0x43 || - cab->stream.next_in[1] != 0x4b) + else if (mszip == 2 && (cab->stream.next_in[0] != 0x43 || + cab->stream.next_in[1] != 0x4b)) goto nomszip; cab->stream.next_in += mszip; cab->stream.avail_in -= mszip; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c index 67d5b21eebb..1c96e6ac195 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c @@ -955,8 +955,7 @@ archive_read_format_cpio_cleanup(struct archive_read *a) while (cpio->links_head != NULL) { struct links_entry *lp = cpio->links_head->next; - if (cpio->links_head->name) - free(cpio->links_head->name); + free(cpio->links_head->name); free(cpio->links_head); cpio->links_head = lp; } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c index 93649f86211..912116675f6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c @@ -41,7 +41,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_iso9660.c 20 #endif #include #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" @@ -1724,8 +1724,7 @@ archive_read_format_iso9660_cleanup(struct archive_read *a) free(iso9660->read_ce_req.reqs); archive_string_free(&iso9660->pathname); archive_string_free(&iso9660->previous_pathname); - if (iso9660->pending_files.files) - free(iso9660->pending_files.files); + free(iso9660->pending_files.files); #ifdef HAVE_ZLIB_H free(iso9660->entry_zisofs.uncompressed_buffer); free(iso9660->entry_zisofs.block_pointers); @@ -2102,6 +2101,7 @@ parse_rockridge(struct archive_read *a, struct file_info *file, const unsigned char *p, const unsigned char *end) { struct iso9660 *iso9660; + int entry_seen = 0; iso9660 = (struct iso9660 *)(a->format->data); @@ -2257,8 +2257,16 @@ parse_rockridge(struct archive_read *a, struct file_info *file, } p += p[2]; + entry_seen = 1; + } + + if (entry_seen) + return (ARCHIVE_OK); + else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Tried to parse Rockridge extensions, but none found"); + return (ARCHIVE_WARN); } - return (ARCHIVE_OK); } static int @@ -3029,8 +3037,7 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap, if (heap->allocated) memcpy(new_pending_files, heap->files, heap->allocated * sizeof(new_pending_files[0])); - if (heap->files != NULL) - free(heap->files); + free(heap->files); heap->files = new_pending_files; heap->allocated = new_size; } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c index 98d02c62d75..1357f9aabf8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c @@ -175,7 +175,9 @@ struct lha { struct archive_string gname; uint16_t header_crc; uint16_t crc; - struct archive_string_conv *sconv; + /* dirname and filename could be in different codepages */ + struct archive_string_conv *sconv_dir; + struct archive_string_conv *sconv_fname; struct archive_string_conv *opt_sconv; struct archive_string dirname; @@ -232,8 +234,8 @@ static time_t lha_dos_time(const unsigned char *); static time_t lha_win_time(uint64_t, long *); static unsigned char lha_calcsum(unsigned char, const void *, int, size_t); -static int lha_parse_linkname(struct archive_string *, - struct archive_string *); +static int lha_parse_linkname(struct archive_wstring *, + struct archive_wstring *); static int lha_read_data_none(struct archive_read *, const void **, size_t *, int64_t *); static int lha_read_data_lzh(struct archive_read *, const void **, @@ -473,13 +475,15 @@ static int archive_read_format_lha_read_header(struct archive_read *a, struct archive_entry *entry) { - struct archive_string linkname; - struct archive_string pathname; + struct archive_wstring linkname; + struct archive_wstring pathname; struct lha *lha; const unsigned char *p; const char *signature; int err; - + struct archive_mstring conv_buffer; + const wchar_t *conv_buffer_p; + lha_crc16_init(); a->archive.archive_format = ARCHIVE_FORMAT_LHA; @@ -561,10 +565,13 @@ archive_read_format_lha_read_header(struct archive_read *a, archive_string_empty(&lha->dirname); archive_string_empty(&lha->filename); lha->dos_attr = 0; - if (lha->opt_sconv != NULL) - lha->sconv = lha->opt_sconv; - else - lha->sconv = NULL; + if (lha->opt_sconv != NULL) { + lha->sconv_dir = lha->opt_sconv; + lha->sconv_fname = lha->opt_sconv; + } else { + lha->sconv_dir = NULL; + lha->sconv_fname = NULL; + } switch (p[H_LEVEL_OFFSET]) { case 0: @@ -594,12 +601,54 @@ archive_read_format_lha_read_header(struct archive_read *a, return (truncated_error(a)); /* - * Make a pathname from a dirname and a filename. - */ - archive_string_concat(&lha->dirname, &lha->filename); + * Make a pathname from a dirname and a filename, after converting to Unicode. + * This is because codepages might differ between dirname and filename. + */ archive_string_init(&pathname); archive_string_init(&linkname); - archive_string_copy(&pathname, &lha->dirname); + archive_string_init(&conv_buffer.aes_mbs); + archive_string_init(&conv_buffer.aes_mbs_in_locale); + archive_string_init(&conv_buffer.aes_utf8); + archive_string_init(&conv_buffer.aes_wcs); + if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->dirname.s, lha->dirname.length, lha->sconv_dir)) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Pathname cannot be converted " + "from %s to Unicode.", + archive_string_conversion_charset_name(lha->sconv_dir)); + err = ARCHIVE_FATAL; + } else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p)) + err = ARCHIVE_FATAL; + if (err == ARCHIVE_FATAL) { + archive_mstring_clean(&conv_buffer); + archive_wstring_free(&pathname); + archive_wstring_free(&linkname); + return (err); + } + archive_wstring_copy(&pathname, &conv_buffer.aes_wcs); + + archive_string_empty(&conv_buffer.aes_mbs); + archive_string_empty(&conv_buffer.aes_mbs_in_locale); + archive_string_empty(&conv_buffer.aes_utf8); + archive_wstring_empty(&conv_buffer.aes_wcs); + if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->filename.s, lha->filename.length, lha->sconv_fname)) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Pathname cannot be converted " + "from %s to Unicode.", + archive_string_conversion_charset_name(lha->sconv_fname)); + err = ARCHIVE_FATAL; + } + else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p)) + err = ARCHIVE_FATAL; + if (err == ARCHIVE_FATAL) { + archive_mstring_clean(&conv_buffer); + archive_wstring_free(&pathname); + archive_wstring_free(&linkname); + return (err); + } + archive_wstring_concat(&pathname, &conv_buffer.aes_wcs); + archive_mstring_clean(&conv_buffer); if ((lha->mode & AE_IFMT) == AE_IFLNK) { /* @@ -610,8 +659,8 @@ archive_read_format_lha_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown symlink-name"); - archive_string_free(&pathname); - archive_string_free(&linkname); + archive_wstring_free(&pathname); + archive_wstring_free(&linkname); return (ARCHIVE_FAILED); } } else { @@ -629,39 +678,13 @@ archive_read_format_lha_read_header(struct archive_read *a, /* * Set basic file parameters. */ - if (archive_entry_copy_pathname_l(entry, pathname.s, - pathname.length, lha->sconv) != 0) { - if (errno == ENOMEM) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for Pathname"); - return (ARCHIVE_FATAL); - } - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Pathname cannot be converted " - "from %s to current locale.", - archive_string_conversion_charset_name(lha->sconv)); - err = ARCHIVE_WARN; - } - archive_string_free(&pathname); + archive_entry_copy_pathname_w(entry, pathname.s); + archive_wstring_free(&pathname); if (archive_strlen(&linkname) > 0) { - if (archive_entry_copy_symlink_l(entry, linkname.s, - linkname.length, lha->sconv) != 0) { - if (errno == ENOMEM) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for Linkname"); - return (ARCHIVE_FATAL); - } - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Linkname cannot be converted " - "from %s to current locale.", - archive_string_conversion_charset_name(lha->sconv)); - err = ARCHIVE_WARN; - } + archive_entry_copy_symlink_w(entry, linkname.s); } else archive_entry_set_symlink(entry, NULL); - archive_string_free(&linkname); + archive_wstring_free(&linkname); /* * When a header level is 0, there is a possibility that * a pathname and a symlink has '\' character, a directory @@ -1208,6 +1231,27 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, archive_strncpy(&lha->filename, (const char *)extdheader, datasize); break; + case EXT_UTF16_FILENAME: + if (datasize == 0) { + /* maybe directory header */ + archive_string_empty(&lha->filename); + break; + } else if (datasize & 1) { + /* UTF-16 characters take always 2 or 4 bytes */ + goto invalid; + } + if (extdheader[0] == '\0') + goto invalid; + archive_string_empty(&lha->filename); + archive_array_append(&lha->filename, + (const char *)extdheader, datasize); + /* Setup a string conversion for a filename. */ + lha->sconv_fname = + archive_string_conversion_from_charset(&a->archive, + "UTF-16LE", 1); + if (lha->sconv_fname == NULL) + return (ARCHIVE_FATAL); + break; case EXT_DIRECTORY: if (datasize == 0 || extdheader[0] == '\0') /* no directory name data. exit this case. */ @@ -1228,6 +1272,50 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, /* invalid directory data */ goto invalid; break; + case EXT_UTF16_DIRECTORY: + /* UTF-16 characters take always 2 or 4 bytes */ + if (datasize == 0 || (datasize & 1) || + extdheader[0] == '\0') { + /* no directory name data. exit this case. */ + goto invalid; + } + + archive_string_empty(&lha->dirname); + archive_array_append(&lha->dirname, + (const char *)extdheader, datasize); + lha->sconv_dir = + archive_string_conversion_from_charset(&a->archive, + "UTF-16LE", 1); + if (lha->sconv_dir == NULL) + return (ARCHIVE_FATAL); + else { + /* + * Convert directory delimiter from 0xFFFF + * to '/' for local system. + */ + uint16_t dirSep; + uint16_t d = 1; + if (archive_be16dec(&d) == 1) + dirSep = 0x2F00; + else + dirSep = 0x002F; + + /* UTF-16LE character */ + uint16_t *utf16name = + (uint16_t *)lha->dirname.s; + for (i = 0; i < lha->dirname.length / 2; i++) { + if (utf16name[i] == 0xFFFF) { + utf16name[i] = dirSep; + } + } + /* Is last character directory separator? */ + if (utf16name[lha->dirname.length / 2 - 1] != + dirSep) { + /* invalid directory data */ + goto invalid; + } + } + break; case EXT_DOS_ATTR: if (datasize == 2) lha->dos_attr = (unsigned char) @@ -1276,11 +1364,16 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, charset = cp.s; break; } - lha->sconv = + lha->sconv_dir = + archive_string_conversion_from_charset( + &(a->archive), charset, 1); + lha->sconv_fname = archive_string_conversion_from_charset( &(a->archive), charset, 1); archive_string_free(&cp); - if (lha->sconv == NULL) + if (lha->sconv_dir == NULL) + return (ARCHIVE_FATAL); + if (lha->sconv_fname == NULL) return (ARCHIVE_FATAL); } break; @@ -1336,8 +1429,7 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, } break; case EXT_TIMEZONE: /* Not supported */ - case EXT_UTF16_FILENAME: /* Not supported */ - case EXT_UTF16_DIRECTORY: /* Not supported */ + break; default: break; } @@ -1600,19 +1692,19 @@ archive_read_format_lha_cleanup(struct archive_read *a) * then a archived pathname is 'xxx/bbb|aaa/bb/cc' */ static int -lha_parse_linkname(struct archive_string *linkname, - struct archive_string *pathname) +lha_parse_linkname(struct archive_wstring *linkname, + struct archive_wstring *pathname) { - char * linkptr; + wchar_t * linkptr; size_t symlen; - linkptr = strchr(pathname->s, '|'); + linkptr = wcschr(pathname->s, L'|'); if (linkptr != NULL) { - symlen = strlen(linkptr + 1); - archive_strncpy(linkname, linkptr+1, symlen); + symlen = wcslen(linkptr + 1); + archive_wstrncpy(linkname, linkptr+1, symlen); *linkptr = 0; - pathname->length = strlen(pathname->s); + pathname->length = wcslen(pathname->s); return (1); } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c index 5b0eadc0844..332944ac51b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c @@ -45,6 +45,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011 #ifdef HAVE_STRING_H #include #endif +#ifdef HAVE_CTYPE_H +#include +#endif #include "archive.h" #include "archive_entry.h" @@ -255,6 +258,7 @@ archive_read_support_format_mtree(struct archive *_a) "Can't allocate mtree data"); return (ARCHIVE_FATAL); } + mtree->checkfs = 0; mtree->fd = -1; __archive_rb_tree_init(&mtree->rbtree, &rb_ops); @@ -1011,7 +1015,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree) { ssize_t len; uintmax_t counter; - char *p; + char *p, *s; struct mtree_option *global; struct mtree_entry *last_entry; int r, is_form_d; @@ -1025,6 +1029,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree) (void)detect_form(a, &is_form_d); for (counter = 1; ; ++counter) { + r = ARCHIVE_OK; len = readline(a, mtree, &p, 65536); if (len == 0) { mtree->this_entry = mtree->entries; @@ -1045,6 +1050,15 @@ read_mtree(struct archive_read *a, struct mtree *mtree) continue; if (*p == '\r' || *p == '\n' || *p == '\0') continue; + /* Non-printable characters are not allowed */ + for (s = p;s < p + len - 1; s++) { + if (!isprint(*s)) { + r = ARCHIVE_FATAL; + break; + } + } + if (r != ARCHIVE_OK) + break; if (*p != '/') { r = process_add_entry(a, mtree, &global, p, len, &last_entry, is_form_d); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c index c68d77b49e5..61f233085ab 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c @@ -32,7 +32,7 @@ #include #include #ifdef HAVE_ZLIB_H -#include /* crc32 */ +#include /* crc32 */ #endif #include "archive.h" @@ -148,6 +148,9 @@ #define FILE_ATTRIBUTE_DIRECTORY 0x10 #endif +#undef minimum +#define minimum(a, b) ((a)<(b)?(a):(b)) + /* Fields common to all headers */ struct rar_header { @@ -258,6 +261,7 @@ struct rar struct data_block_offsets *dbo; unsigned int cursor; unsigned int nodes; + char filename_must_match; /* LZSS members */ struct huffman_code maincode; @@ -1023,8 +1027,11 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, case COMPRESS_METHOD_GOOD: case COMPRESS_METHOD_BEST: ret = read_data_compressed(a, buff, size, offset); - if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) + if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) { __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); + rar->start_new_table = 1; + rar->ppmd_valid = 0; + } break; default: @@ -1560,6 +1567,12 @@ read_header(struct archive_read *a, struct archive_entry *entry, } return ret; } + else if (rar->filename_must_match) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Mismatch of file parts split across multi-volume archive"); + return (ARCHIVE_FATAL); + } rar->filename_save = (char*)realloc(rar->filename_save, filename_size + 1); @@ -1712,6 +1725,13 @@ read_exttime(const char *p, struct rar *rar, const char *endp) struct tm *tm; time_t t; long nsec; +#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) + struct tm tmbuf; +#endif +#if defined(HAVE__LOCALTIME64_S) + errno_t terr; + __time64_t tmptime; +#endif if (p + 2 > endp) return (-1); @@ -1743,7 +1763,18 @@ read_exttime(const char *p, struct rar *rar, const char *endp) rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8); p++; } +#if defined(HAVE_LOCALTIME_R) + tm = localtime_r(&t, &tmbuf); +#elif defined(HAVE__LOCALTIME64_S) + tmptime = t; + terr = _localtime64_s(&tmbuf, &tmptime); + if (terr) + tm = NULL; + else + tm = &tmbuf; +#else tm = localtime(&t); +#endif nsec = tm->tm_sec + rem / NS_UNIT; if (rmode & 4) { @@ -2300,6 +2331,11 @@ parse_codes(struct archive_read *a) new_size = DICTIONARY_MAX_SIZE; else new_size = rar_fls((unsigned int)rar->unp_size) << 1; + if (new_size == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Zero window size is invalid."); + return (ARCHIVE_FATAL); + } new_window = realloc(rar->lzss.window, new_size); if (new_window == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -2437,8 +2473,11 @@ create_code(struct archive_read *a, struct huffman_code *code, if (add_value(a, code, j, codebits, i) != ARCHIVE_OK) return (ARCHIVE_FATAL); codebits++; - if (--symbolsleft <= 0) { break; break; } + if (--symbolsleft <= 0) + break; } + if (symbolsleft <= 0) + break; codebits <<= 1; } return (ARCHIVE_OK); @@ -2448,7 +2487,8 @@ static int add_value(struct archive_read *a, struct huffman_code *code, int value, int codebits, int length) { - int repeatpos, lastnode, bitpos, bit, repeatnode, nextnode; + int lastnode, bitpos, bit; + /* int repeatpos, repeatnode, nextnode; */ free(code->table); code->table = NULL; @@ -2458,6 +2498,9 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, if(length < code->minlength) code->minlength = length; + /* + * Dead code, repeatpos was is -1 + * repeatpos = -1; if (repeatpos == 0 || (repeatpos >= 0 && (((codebits >> (repeatpos - 1)) & 3) == 0 @@ -2467,6 +2510,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, "Invalid repeat position"); return (ARCHIVE_FATAL); } + */ lastnode = 0; for (bitpos = length - 1; bitpos >= 0; bitpos--) @@ -2482,9 +2526,12 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, return (ARCHIVE_FATAL); } + /* + * Dead code, repeatpos was -1, bitpos >=0 + * if (bitpos == repeatpos) { - /* Open branch check */ + * Open branch check * if (!(code->tree[lastnode].branches[bit] < 0)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -2503,16 +2550,17 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, return (ARCHIVE_FATAL); } - /* Set branches */ + * Set branches * code->tree[lastnode].branches[bit] = repeatnode; code->tree[repeatnode].branches[bit] = repeatnode; code->tree[repeatnode].branches[bit^1] = nextnode; lastnode = nextnode; - bitpos++; /* terminating bit already handled, skip it */ + bitpos++; * terminating bit already handled, skip it * } else { + */ /* Open branch check */ if (code->tree[lastnode].branches[bit] < 0) { @@ -2526,7 +2574,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, /* set to branch */ lastnode = code->tree[lastnode].branches[bit]; - } + /* } */ } if (!(code->tree[lastnode].branches[0] == -1 @@ -2610,11 +2658,15 @@ make_table_recurse(struct archive_read *a, struct huffman_code *code, int node, table[i].value = code->tree[node].branches[0]; } } + /* + * Dead code, node >= 0 + * else if (node < 0) { for(i = 0; i < currtablesize; i++) table[i].length = -1; } + */ else { if(depth == maxdepth) @@ -2646,6 +2698,10 @@ expand(struct archive_read *a, int64_t end) 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; + static const int lengthb_min = minimum( + (int)(sizeof(lengthbases)/sizeof(lengthbases[0])), + (int)(sizeof(lengthbits)/sizeof(lengthbits[0])) + ); static const unsigned int offsetbases[] = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, @@ -2663,6 +2719,10 @@ expand(struct archive_read *a, int64_t end) 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 }; + static const int offsetb_min = minimum( + (int)(sizeof(offsetbases)/sizeof(offsetbases[0])), + (int)(sizeof(offsetbits)/sizeof(offsetbits[0])) + ); static const unsigned char shortbases[] = { 0, 4, 8, 16, 32, 64, 128, 192 }; static const unsigned char shortbits[] = @@ -2742,9 +2802,7 @@ expand(struct archive_read *a, int64_t end) if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0) goto bad_data; - if (lensymbol > (int)(sizeof(lengthbases)/sizeof(lengthbases[0]))) - goto bad_data; - if (lensymbol > (int)(sizeof(lengthbits)/sizeof(lengthbits[0]))) + if (lensymbol > lengthb_min) goto bad_data; len = lengthbases[lensymbol] + 2; if (lengthbits[lensymbol] > 0) { @@ -2776,9 +2834,7 @@ expand(struct archive_read *a, int64_t end) } else { - if (symbol-271 > (int)(sizeof(lengthbases)/sizeof(lengthbases[0]))) - goto bad_data; - if (symbol-271 > (int)(sizeof(lengthbits)/sizeof(lengthbits[0]))) + if (symbol-271 > lengthb_min) goto bad_data; len = lengthbases[symbol-271]+3; if(lengthbits[symbol-271] > 0) { @@ -2790,9 +2846,7 @@ expand(struct archive_read *a, int64_t end) if ((offssymbol = read_next_symbol(a, &rar->offsetcode)) < 0) goto bad_data; - if (offssymbol > (int)(sizeof(offsetbases)/sizeof(offsetbases[0]))) - goto bad_data; - if (offssymbol > (int)(sizeof(offsetbits)/sizeof(offsetbits[0]))) + if (offssymbol > offsetb_min) goto bad_data; offs = offsetbases[offssymbol]+1; if(offsetbits[offssymbol] > 0) @@ -2928,12 +2982,14 @@ rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) else if (*avail == 0 && rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER) { + rar->filename_must_match = 1; ret = archive_read_format_rar_read_header(a, a->entry); if (ret == (ARCHIVE_EOF)) { rar->has_endarc_header = 1; ret = archive_read_format_rar_read_header(a, a->entry); } + rar->filename_must_match = 0; if (ret != (ARCHIVE_OK)) return NULL; return rar_read_ahead(a, min, avail); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c new file mode 100644 index 00000000000..d3a1c1b2ccd --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c @@ -0,0 +1,4103 @@ +/*- +* Copyright (c) 2018 Grzegorz Antoniak (http://antoniak.org) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "archive_platform.h" +#include "archive_endian.h" + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_ZLIB_H +#include /* crc32 */ +#endif +#ifdef HAVE_LIMITS_H +#include +#endif + +#include "archive.h" +#ifndef HAVE_ZLIB_H +#include "archive_crc32.h" +#endif + +#include "archive_entry.h" +#include "archive_entry_locale.h" +#include "archive_ppmd7_private.h" +#include "archive_entry_private.h" + +#ifdef HAVE_BLAKE2_H +#include +#else +#include "archive_blake2.h" +#endif + +/*#define CHECK_CRC_ON_SOLID_SKIP*/ +/*#define DONT_FAIL_ON_CRC_ERROR*/ +/*#define DEBUG*/ + +#define rar5_min(a, b) (((a) > (b)) ? (b) : (a)) +#define rar5_max(a, b) (((a) > (b)) ? (a) : (b)) +#define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X))) + +#if defined DEBUG +#define DEBUG_CODE if(1) +#define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0) +#else +#define DEBUG_CODE if(0) +#endif + +/* Real RAR5 magic number is: + * + * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 + * "Rar!→•☺·\x00" + * + * Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't + * want to put this magic sequence in each binary that uses libarchive, so + * applications that scan through the file for this marker won't trigger on + * this "false" one. + * + * The array itself is decrypted in `rar5_init` function. */ + +static unsigned char rar5_signature_xor[] = { 243, 192, 211, 128, 187, 166, 160, 161 }; +static const size_t g_unpack_window_size = 0x20000; + +/* These could have been static const's, but they aren't, because of + * Visual Studio. */ +#define MAX_NAME_IN_CHARS 2048 +#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS) + +struct file_header { + ssize_t bytes_remaining; + ssize_t unpacked_size; + int64_t last_offset; /* Used in sanity checks. */ + int64_t last_size; /* Used in sanity checks. */ + + uint8_t solid : 1; /* Is this a solid stream? */ + uint8_t service : 1; /* Is this file a service data? */ + uint8_t eof : 1; /* Did we finish unpacking the file? */ + uint8_t dir : 1; /* Is this file entry a directory? */ + + /* Optional time fields. */ + uint64_t e_mtime; + uint64_t e_ctime; + uint64_t e_atime; + uint32_t e_unix_ns; + + /* Optional hash fields. */ + uint32_t stored_crc32; + uint32_t calculated_crc32; + uint8_t blake2sp[32]; + blake2sp_state b2state; + char has_blake2; + + /* Optional redir fields */ + uint64_t redir_type; + uint64_t redir_flags; + + ssize_t solid_window_size; /* Used in file format check. */ +}; + +enum EXTRA { + EX_CRYPT = 0x01, + EX_HASH = 0x02, + EX_HTIME = 0x03, + EX_VERSION = 0x04, + EX_REDIR = 0x05, + EX_UOWNER = 0x06, + EX_SUBDATA = 0x07 +}; + +#define REDIR_SYMLINK_IS_DIR 1 + +enum REDIR_TYPE { + REDIR_TYPE_NONE = 0, + REDIR_TYPE_UNIXSYMLINK = 1, + REDIR_TYPE_WINSYMLINK = 2, + REDIR_TYPE_JUNCTION = 3, + REDIR_TYPE_HARDLINK = 4, + REDIR_TYPE_FILECOPY = 5, +}; + +#define OWNER_USER_NAME 0x01 +#define OWNER_GROUP_NAME 0x02 +#define OWNER_USER_UID 0x04 +#define OWNER_GROUP_GID 0x08 +#define OWNER_MAXNAMELEN 256 + +enum FILTER_TYPE { + FILTER_DELTA = 0, /* Generic pattern. */ + FILTER_E8 = 1, /* Intel x86 code. */ + FILTER_E8E9 = 2, /* Intel x86 code. */ + FILTER_ARM = 3, /* ARM code. */ + FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */ + FILTER_RGB = 5, /* Color palette, not used in RARv5. */ + FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */ + FILTER_PPM = 7, /* Predictive pattern matching, not used in + RARv5. */ + FILTER_NONE = 8, +}; + +struct filter_info { + int type; + int channels; + int pos_r; + + int64_t block_start; + ssize_t block_length; + uint16_t width; +}; + +struct data_ready { + char used; + const uint8_t* buf; + size_t size; + int64_t offset; +}; + +struct cdeque { + uint16_t beg_pos; + uint16_t end_pos; + uint16_t cap_mask; + uint16_t size; + size_t* arr; +}; + +struct decode_table { + uint32_t size; + int32_t decode_len[16]; + uint32_t decode_pos[16]; + uint32_t quick_bits; + uint8_t quick_len[1 << 10]; + uint16_t quick_num[1 << 10]; + uint16_t decode_num[306]; +}; + +struct comp_state { + /* Flag used to specify if unpacker needs to reinitialize the + uncompression context. */ + uint8_t initialized : 1; + + /* Flag used when applying filters. */ + uint8_t all_filters_applied : 1; + + /* Flag used to skip file context reinitialization, used when unpacker + is skipping through different multivolume archives. */ + uint8_t switch_multivolume : 1; + + /* Flag used to specify if unpacker has processed the whole data block + or just a part of it. */ + uint8_t block_parsing_finished : 1; + + signed int notused : 4; + + int flags; /* Uncompression flags. */ + int method; /* Uncompression algorithm method. */ + int version; /* Uncompression algorithm version. */ + ssize_t window_size; /* Size of window_buf. */ + uint8_t* window_buf; /* Circular buffer used during + decompression. */ + uint8_t* filtered_buf; /* Buffer used when applying filters. */ + const uint8_t* block_buf; /* Buffer used when merging blocks. */ + size_t window_mask; /* Convenience field; window_size - 1. */ + int64_t write_ptr; /* This amount of data has been unpacked + in the window buffer. */ + int64_t last_write_ptr; /* This amount of data has been stored in + the output file. */ + int64_t last_unstore_ptr; /* Counter of bytes extracted during + unstoring. This is separate from + last_write_ptr because of how SERVICE + base blocks are handled during skipping + in solid multiarchive archives. */ + int64_t solid_offset; /* Additional offset inside the window + buffer, used in unpacking solid + archives. */ + ssize_t cur_block_size; /* Size of current data block. */ + int last_len; /* Flag used in lzss decompression. */ + + /* Decode tables used during lzss uncompression. */ + +#define HUFF_BC 20 + struct decode_table bd; /* huffman bit lengths */ +#define HUFF_NC 306 + struct decode_table ld; /* literals */ +#define HUFF_DC 64 + struct decode_table dd; /* distances */ +#define HUFF_LDC 16 + struct decode_table ldd; /* lower bits of distances */ +#define HUFF_RC 44 + struct decode_table rd; /* repeating distances */ +#define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC) + + /* Circular deque for storing filters. */ + struct cdeque filters; + int64_t last_block_start; /* Used for sanity checking. */ + ssize_t last_block_length; /* Used for sanity checking. */ + + /* Distance cache used during lzss uncompression. */ + int dist_cache[4]; + + /* Data buffer stack. */ + struct data_ready dready[2]; +}; + +/* Bit reader state. */ +struct bit_reader { + int8_t bit_addr; /* Current bit pointer inside current byte. */ + int in_addr; /* Current byte pointer. */ +}; + +/* RARv5 block header structure. Use bf_* functions to get values from + * block_flags_u8 field. I.e. bf_byte_count, etc. */ +struct compressed_block_header { + /* block_flags_u8 contain fields encoded in little-endian bitfield: + * + * - table present flag (shr 7, and 1), + * - last block flag (shr 6, and 1), + * - byte_count (shr 3, and 7), + * - bit_size (shr 0, and 7). + */ + uint8_t block_flags_u8; + uint8_t block_cksum; +}; + +/* RARv5 main header structure. */ +struct main_header { + /* Does the archive contain solid streams? */ + uint8_t solid : 1; + + /* If this a multi-file archive? */ + uint8_t volume : 1; + uint8_t endarc : 1; + uint8_t notused : 5; + + unsigned int vol_no; +}; + +struct generic_header { + uint8_t split_after : 1; + uint8_t split_before : 1; + uint8_t padding : 6; + int size; + int last_header_id; +}; + +struct multivolume { + unsigned int expected_vol_no; + uint8_t* push_buf; +}; + +/* Main context structure. */ +struct rar5 { + int header_initialized; + + /* Set to 1 if current file is positioned AFTER the magic value + * of the archive file. This is used in header reading functions. */ + int skipped_magic; + + /* Set to not zero if we're in skip mode (either by calling + * rar5_data_skip function or when skipping over solid streams). + * Set to 0 when in * extraction mode. This is used during checksum + * calculation functions. */ + int skip_mode; + + /* Set to not zero if we're in block merging mode (i.e. when switching + * to another file in multivolume archive, last block from 1st archive + * needs to be merged with 1st block from 2nd archive). This flag + * guards against recursive use of the merging function, which doesn't + * support recursive calls. */ + int merge_mode; + + /* An offset to QuickOpen list. This is not supported by this unpacker, + * because we're focusing on streaming interface. QuickOpen is designed + * to make things quicker for non-stream interfaces, so it's not our + * use case. */ + uint64_t qlist_offset; + + /* An offset to additional Recovery data. This is not supported by this + * unpacker. Recovery data are additional Reed-Solomon codes that could + * be used to calculate bytes that are missing in archive or are + * corrupted. */ + uint64_t rr_offset; + + /* Various context variables grouped to different structures. */ + struct generic_header generic; + struct main_header main; + struct comp_state cstate; + struct file_header file; + struct bit_reader bits; + struct multivolume vol; + + /* The header of currently processed RARv5 block. Used in main + * decompression logic loop. */ + struct compressed_block_header last_block_hdr; +}; + +/* Forward function declarations. */ + +static void rar5_signature(char *buf); +static int verify_global_checksums(struct archive_read* a); +static int rar5_read_data_skip(struct archive_read *a); +static int push_data_ready(struct archive_read* a, struct rar5* rar, + const uint8_t* buf, size_t size, int64_t offset); + +/* CDE_xxx = Circular Double Ended (Queue) return values. */ +enum CDE_RETURN_VALUES { + CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS, +}; + +/* Clears the contents of this circular deque. */ +static void cdeque_clear(struct cdeque* d) { + d->size = 0; + d->beg_pos = 0; + d->end_pos = 0; +} + +/* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32, + * 64, 256, etc. When the user will add another item above current capacity, + * the circular deque will overwrite the oldest entry. */ +static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) { + if(d == NULL || max_capacity_power_of_2 == 0) + return CDE_PARAM; + + d->cap_mask = max_capacity_power_of_2 - 1; + d->arr = NULL; + + if((max_capacity_power_of_2 & d->cap_mask) != 0) + return CDE_PARAM; + + cdeque_clear(d); + d->arr = malloc(sizeof(void*) * max_capacity_power_of_2); + + return d->arr ? CDE_OK : CDE_ALLOC; +} + +/* Return the current size (not capacity) of circular deque `d`. */ +static size_t cdeque_size(struct cdeque* d) { + return d->size; +} + +/* Returns the first element of current circular deque. Note that this function + * doesn't perform any bounds checking. If you need bounds checking, use + * `cdeque_front()` function instead. */ +static void cdeque_front_fast(struct cdeque* d, void** value) { + *value = (void*) d->arr[d->beg_pos]; +} + +/* Returns the first element of current circular deque. This function + * performs bounds checking. */ +static int cdeque_front(struct cdeque* d, void** value) { + if(d->size > 0) { + cdeque_front_fast(d, value); + return CDE_OK; + } else + return CDE_OUT_OF_BOUNDS; +} + +/* Pushes a new element into the end of this circular deque object. If current + * size will exceed capacity, the oldest element will be overwritten. */ +static int cdeque_push_back(struct cdeque* d, void* item) { + if(d == NULL) + return CDE_PARAM; + + if(d->size == d->cap_mask + 1) + return CDE_OUT_OF_BOUNDS; + + d->arr[d->end_pos] = (size_t) item; + d->end_pos = (d->end_pos + 1) & d->cap_mask; + d->size++; + + return CDE_OK; +} + +/* Pops a front element of this circular deque object and returns its value. + * This function doesn't perform any bounds checking. */ +static void cdeque_pop_front_fast(struct cdeque* d, void** value) { + *value = (void*) d->arr[d->beg_pos]; + d->beg_pos = (d->beg_pos + 1) & d->cap_mask; + d->size--; +} + +/* Pops a front element of this circular deque object and returns its value. + * This function performs bounds checking. */ +static int cdeque_pop_front(struct cdeque* d, void** value) { + if(!d || !value) + return CDE_PARAM; + + if(d->size == 0) + return CDE_OUT_OF_BOUNDS; + + cdeque_pop_front_fast(d, value); + return CDE_OK; +} + +/* Convenience function to cast filter_info** to void **. */ +static void** cdeque_filter_p(struct filter_info** f) { + return (void**) (size_t) f; +} + +/* Convenience function to cast filter_info* to void *. */ +static void* cdeque_filter(struct filter_info* f) { + return (void**) (size_t) f; +} + +/* Destroys this circular deque object. Deallocates the memory of the + * collection buffer, but doesn't deallocate the memory of any pointer passed + * to this deque as a value. */ +static void cdeque_free(struct cdeque* d) { + if(!d) + return; + + if(!d->arr) + return; + + free(d->arr); + + d->arr = NULL; + d->beg_pos = -1; + d->end_pos = -1; + d->cap_mask = 0; +} + +static inline +uint8_t bf_bit_size(const struct compressed_block_header* hdr) { + return hdr->block_flags_u8 & 7; +} + +static inline +uint8_t bf_byte_count(const struct compressed_block_header* hdr) { + return (hdr->block_flags_u8 >> 3) & 7; +} + +static inline +uint8_t bf_is_table_present(const struct compressed_block_header* hdr) { + return (hdr->block_flags_u8 >> 7) & 1; +} + +static inline struct rar5* get_context(struct archive_read* a) { + return (struct rar5*) a->format->data; +} + +/* Convenience functions used by filter implementations. */ +static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask, + int64_t start, int64_t end) +{ + if((start & mask) > (end & mask)) { + ssize_t len1 = mask + 1 - (start & mask); + ssize_t len2 = end & mask; + + memcpy(dst, &window[start & mask], len1); + memcpy(dst + len1, window, len2); + } else { + memcpy(dst, &window[start & mask], (size_t) (end - start)); + } +} + +static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) { + uint8_t linear_buf[4]; + circular_memcpy(linear_buf, rar->cstate.window_buf, + rar->cstate.window_mask, offset, offset + 4); + return archive_le32dec(linear_buf); +} + +static void write_filter_data(struct rar5* rar, uint32_t offset, + uint32_t value) +{ + archive_le32enc(&rar->cstate.filtered_buf[offset], value); +} + +/* Allocates a new filter descriptor and adds it to the filter array. */ +static struct filter_info* add_new_filter(struct rar5* rar) { + struct filter_info* f = + (struct filter_info*) calloc(1, sizeof(struct filter_info)); + + if(!f) { + return NULL; + } + + cdeque_push_back(&rar->cstate.filters, cdeque_filter(f)); + return f; +} + +static int run_delta_filter(struct rar5* rar, struct filter_info* flt) { + int i; + ssize_t dest_pos, src_pos = 0; + + for(i = 0; i < flt->channels; i++) { + uint8_t prev_byte = 0; + for(dest_pos = i; + dest_pos < flt->block_length; + dest_pos += flt->channels) + { + uint8_t byte; + + byte = rar->cstate.window_buf[ + (rar->cstate.solid_offset + flt->block_start + + src_pos) & rar->cstate.window_mask]; + + prev_byte -= byte; + rar->cstate.filtered_buf[dest_pos] = prev_byte; + src_pos++; + } + } + + return ARCHIVE_OK; +} + +static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, + int extended) +{ + const uint32_t file_size = 0x1000000; + ssize_t i; + + circular_memcpy(rar->cstate.filtered_buf, + rar->cstate.window_buf, rar->cstate.window_mask, + rar->cstate.solid_offset + flt->block_start, + rar->cstate.solid_offset + flt->block_start + flt->block_length); + + for(i = 0; i < flt->block_length - 4;) { + uint8_t b = rar->cstate.window_buf[ + (rar->cstate.solid_offset + flt->block_start + + i++) & rar->cstate.window_mask]; + + /* + * 0xE8 = x86's call (function call) + * 0xE9 = x86's jmp (unconditional jump) + */ + if(b == 0xE8 || (extended && b == 0xE9)) { + + uint32_t addr; + uint32_t offset = (i + flt->block_start) % file_size; + + addr = read_filter_data(rar, + (uint32_t)(rar->cstate.solid_offset + + flt->block_start + i) & rar->cstate.window_mask); + + if(addr & 0x80000000) { + if(((addr + offset) & 0x80000000) == 0) { + write_filter_data(rar, (uint32_t)i, + addr + file_size); + } + } else { + if((addr - file_size) & 0x80000000) { + uint32_t naddr = addr - offset; + write_filter_data(rar, (uint32_t)i, + naddr); + } + } + + i += 4; + } + } + + return ARCHIVE_OK; +} + +static int run_arm_filter(struct rar5* rar, struct filter_info* flt) { + ssize_t i = 0; + uint32_t offset; + + circular_memcpy(rar->cstate.filtered_buf, + rar->cstate.window_buf, rar->cstate.window_mask, + rar->cstate.solid_offset + flt->block_start, + rar->cstate.solid_offset + flt->block_start + flt->block_length); + + for(i = 0; i < flt->block_length - 3; i += 4) { + uint8_t* b = &rar->cstate.window_buf[ + (rar->cstate.solid_offset + + flt->block_start + i + 3) & rar->cstate.window_mask]; + + if(*b == 0xEB) { + /* 0xEB = ARM's BL (branch + link) instruction. */ + offset = read_filter_data(rar, + (rar->cstate.solid_offset + flt->block_start + i) & + rar->cstate.window_mask) & 0x00ffffff; + + offset -= (uint32_t) ((i + flt->block_start) / 4); + offset = (offset & 0x00ffffff) | 0xeb000000; + write_filter_data(rar, (uint32_t)i, offset); + } + } + + return ARCHIVE_OK; +} + +static int run_filter(struct archive_read* a, struct filter_info* flt) { + int ret; + struct rar5* rar = get_context(a); + + free(rar->cstate.filtered_buf); + + rar->cstate.filtered_buf = malloc(flt->block_length); + if(!rar->cstate.filtered_buf) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for filter data."); + return ARCHIVE_FATAL; + } + + switch(flt->type) { + case FILTER_DELTA: + ret = run_delta_filter(rar, flt); + break; + + case FILTER_E8: + /* fallthrough */ + case FILTER_E8E9: + ret = run_e8e9_filter(rar, flt, + flt->type == FILTER_E8E9); + break; + + case FILTER_ARM: + ret = run_arm_filter(rar, flt); + break; + + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported filter type: 0x%x", flt->type); + return ARCHIVE_FATAL; + } + + if(ret != ARCHIVE_OK) { + /* Filter has failed. */ + return ret; + } + + if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf, + flt->block_length, rar->cstate.last_write_ptr)) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Stack overflow when submitting unpacked data"); + + return ARCHIVE_FATAL; + } + + rar->cstate.last_write_ptr += flt->block_length; + return ARCHIVE_OK; +} + +/* The `push_data` function submits the selected data range to the user. + * Next call of `use_data` will use the pointer, size and offset arguments + * that are specified here. These arguments are pushed to the FIFO stack here, + * and popped from the stack by the `use_data` function. */ +static void push_data(struct archive_read* a, struct rar5* rar, + const uint8_t* buf, int64_t idx_begin, int64_t idx_end) +{ + const uint64_t wmask = rar->cstate.window_mask; + const ssize_t solid_write_ptr = (rar->cstate.solid_offset + + rar->cstate.last_write_ptr) & wmask; + + idx_begin += rar->cstate.solid_offset; + idx_end += rar->cstate.solid_offset; + + /* Check if our unpacked data is wrapped inside the window circular + * buffer. If it's not wrapped, it can be copied out by using + * a single memcpy, but when it's wrapped, we need to copy the first + * part with one memcpy, and the second part with another memcpy. */ + + if((idx_begin & wmask) > (idx_end & wmask)) { + /* The data is wrapped (begin offset sis bigger than end + * offset). */ + const ssize_t frag1_size = rar->cstate.window_size - + (idx_begin & wmask); + const ssize_t frag2_size = idx_end & wmask; + + /* Copy the first part of the buffer first. */ + push_data_ready(a, rar, buf + solid_write_ptr, frag1_size, + rar->cstate.last_write_ptr); + + /* Copy the second part of the buffer. */ + push_data_ready(a, rar, buf, frag2_size, + rar->cstate.last_write_ptr + frag1_size); + + rar->cstate.last_write_ptr += frag1_size + frag2_size; + } else { + /* Data is not wrapped, so we can just use one call to copy the + * data. */ + push_data_ready(a, rar, + buf + solid_write_ptr, (idx_end - idx_begin) & wmask, + rar->cstate.last_write_ptr); + + rar->cstate.last_write_ptr += idx_end - idx_begin; + } +} + +/* Convenience function that submits the data to the user. It uses the + * unpack window buffer as a source location. */ +static void push_window_data(struct archive_read* a, struct rar5* rar, + int64_t idx_begin, int64_t idx_end) +{ + push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end); +} + +static int apply_filters(struct archive_read* a) { + struct filter_info* flt; + struct rar5* rar = get_context(a); + int ret; + + rar->cstate.all_filters_applied = 0; + + /* Get the first filter that can be applied to our data. The data + * needs to be fully unpacked before the filter can be run. */ + if(CDE_OK == cdeque_front(&rar->cstate.filters, + cdeque_filter_p(&flt))) { + /* Check if our unpacked data fully covers this filter's + * range. */ + if(rar->cstate.write_ptr > flt->block_start && + rar->cstate.write_ptr >= flt->block_start + + flt->block_length) { + /* Check if we have some data pending to be written + * right before the filter's start offset. */ + if(rar->cstate.last_write_ptr == flt->block_start) { + /* Run the filter specified by descriptor + * `flt`. */ + ret = run_filter(a, flt); + if(ret != ARCHIVE_OK) { + /* Filter failure, return error. */ + return ret; + } + + /* Filter descriptor won't be needed anymore + * after it's used, * so remove it from the + * filter list and free its memory. */ + (void) cdeque_pop_front(&rar->cstate.filters, + cdeque_filter_p(&flt)); + + free(flt); + } else { + /* We can't run filters yet, dump the memory + * right before the filter. */ + push_window_data(a, rar, + rar->cstate.last_write_ptr, + flt->block_start); + } + + /* Return 'filter applied or not needed' state to the + * caller. */ + return ARCHIVE_RETRY; + } + } + + rar->cstate.all_filters_applied = 1; + return ARCHIVE_OK; +} + +static void dist_cache_push(struct rar5* rar, int value) { + int* q = rar->cstate.dist_cache; + + q[3] = q[2]; + q[2] = q[1]; + q[1] = q[0]; + q[0] = value; +} + +static int dist_cache_touch(struct rar5* rar, int idx) { + int* q = rar->cstate.dist_cache; + int i, dist = q[idx]; + + for(i = idx; i > 0; i--) + q[i] = q[i - 1]; + + q[0] = dist; + return dist; +} + +static void free_filters(struct rar5* rar) { + struct cdeque* d = &rar->cstate.filters; + + /* Free any remaining filters. All filters should be naturally + * consumed by the unpacking function, so remaining filters after + * unpacking normally mean that unpacking wasn't successful. + * But still of course we shouldn't leak memory in such case. */ + + /* cdeque_size() is a fast operation, so we can use it as a loop + * expression. */ + while(cdeque_size(d) > 0) { + struct filter_info* f = NULL; + + /* Pop_front will also decrease the collection's size. */ + if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f))) + free(f); + } + + cdeque_clear(d); + + /* Also clear out the variables needed for sanity checking. */ + rar->cstate.last_block_start = 0; + rar->cstate.last_block_length = 0; +} + +static void reset_file_context(struct rar5* rar) { + memset(&rar->file, 0, sizeof(rar->file)); + blake2sp_init(&rar->file.b2state, 32); + + if(rar->main.solid) { + rar->cstate.solid_offset += rar->cstate.write_ptr; + } else { + rar->cstate.solid_offset = 0; + } + + rar->cstate.write_ptr = 0; + rar->cstate.last_write_ptr = 0; + rar->cstate.last_unstore_ptr = 0; + + rar->file.redir_type = REDIR_TYPE_NONE; + rar->file.redir_flags = 0; + + free_filters(rar); +} + +static inline int get_archive_read(struct archive* a, + struct archive_read** ar) +{ + *ar = (struct archive_read*) a; + archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_support_format_rar5"); + + return ARCHIVE_OK; +} + +static int read_ahead(struct archive_read* a, size_t how_many, + const uint8_t** ptr) +{ + ssize_t avail = -1; + if(!ptr) + return 0; + + *ptr = __archive_read_ahead(a, how_many, &avail); + if(*ptr == NULL) { + return 0; + } + + return 1; +} + +static int consume(struct archive_read* a, int64_t how_many) { + int ret; + + ret = how_many == __archive_read_consume(a, how_many) + ? ARCHIVE_OK + : ARCHIVE_FATAL; + + return ret; +} + +/** + * Read a RAR5 variable sized numeric value. This value will be stored in + * `pvalue`. The `pvalue_len` argument points to a variable that will receive + * the byte count that was consumed in order to decode the `pvalue` value, plus + * one. + * + * pvalue_len is optional and can be NULL. + * + * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume + * the number of bytes that `pvalue_len` value contains. If the `pvalue_len` + * is NULL, this consuming operation is done automatically. + * + * Returns 1 if *pvalue was successfully read. + * Returns 0 if there was an error. In this case, *pvalue contains an + * invalid value. + */ + +static int read_var(struct archive_read* a, uint64_t* pvalue, + uint64_t* pvalue_len) +{ + uint64_t result = 0; + size_t shift, i; + const uint8_t* p; + uint8_t b; + + /* We will read maximum of 8 bytes. We don't have to handle the + * situation to read the RAR5 variable-sized value stored at the end of + * the file, because such situation will never happen. */ + if(!read_ahead(a, 8, &p)) + return 0; + + for(shift = 0, i = 0; i < 8; i++, shift += 7) { + b = p[i]; + + /* Strip the MSB from the input byte and add the resulting + * number to the `result`. */ + result += (b & (uint64_t)0x7F) << shift; + + /* MSB set to 1 means we need to continue decoding process. + * MSB set to 0 means we're done. + * + * This conditional checks for the second case. */ + if((b & 0x80) == 0) { + if(pvalue) { + *pvalue = result; + } + + /* If the caller has passed the `pvalue_len` pointer, + * store the number of consumed bytes in it and do NOT + * consume those bytes, since the caller has all the + * information it needs to perform */ + if(pvalue_len) { + *pvalue_len = 1 + i; + } else { + /* If the caller did not provide the + * `pvalue_len` pointer, it will not have the + * possibility to advance the file pointer, + * because it will not know how many bytes it + * needs to consume. This is why we handle + * such situation here automatically. */ + if(ARCHIVE_OK != consume(a, 1 + i)) { + return 0; + } + } + + /* End of decoding process, return success. */ + return 1; + } + } + + /* The decoded value takes the maximum number of 8 bytes. + * It's a maximum number of bytes, so end decoding process here + * even if the first bit of last byte is 1. */ + if(pvalue) { + *pvalue = result; + } + + if(pvalue_len) { + *pvalue_len = 9; + } else { + if(ARCHIVE_OK != consume(a, 9)) { + return 0; + } + } + + return 1; +} + +static int read_var_sized(struct archive_read* a, size_t* pvalue, + size_t* pvalue_len) +{ + uint64_t v; + uint64_t v_size = 0; + + const int ret = pvalue_len ? read_var(a, &v, &v_size) + : read_var(a, &v, NULL); + + if(ret == 1 && pvalue) { + *pvalue = (size_t) v; + } + + if(pvalue_len) { + /* Possible data truncation should be safe. */ + *pvalue_len = (size_t) v_size; + } + + return ret; +} + +static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) { + uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24; + bits |= p[rar->bits.in_addr + 1] << 16; + bits |= p[rar->bits.in_addr + 2] << 8; + bits |= p[rar->bits.in_addr + 3]; + bits <<= rar->bits.bit_addr; + bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr); + *value = bits; + return ARCHIVE_OK; +} + +static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) { + int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16; + bits |= (int) p[rar->bits.in_addr + 1] << 8; + bits |= (int) p[rar->bits.in_addr + 2]; + bits >>= (8 - rar->bits.bit_addr); + *value = bits & 0xffff; + return ARCHIVE_OK; +} + +static void skip_bits(struct rar5* rar, int bits) { + const int new_bits = rar->bits.bit_addr + bits; + rar->bits.in_addr += new_bits >> 3; + rar->bits.bit_addr = new_bits & 7; +} + +/* n = up to 16 */ +static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n, + int* value) +{ + uint16_t v; + int ret, num; + + if(n == 0 || n > 16) { + /* This is a programmer error and should never happen + * in runtime. */ + return ARCHIVE_FATAL; + } + + ret = read_bits_16(rar, p, &v); + if(ret != ARCHIVE_OK) + return ret; + + num = (int) v; + num >>= 16 - n; + + skip_bits(rar, n); + + if(value) + *value = num; + + return ARCHIVE_OK; +} + +static int read_u32(struct archive_read* a, uint32_t* pvalue) { + const uint8_t* p; + if(!read_ahead(a, 4, &p)) + return 0; + + *pvalue = archive_le32dec(p); + return ARCHIVE_OK == consume(a, 4) ? 1 : 0; +} + +static int read_u64(struct archive_read* a, uint64_t* pvalue) { + const uint8_t* p; + if(!read_ahead(a, 8, &p)) + return 0; + + *pvalue = archive_le64dec(p); + return ARCHIVE_OK == consume(a, 8) ? 1 : 0; +} + +static int bid_standard(struct archive_read* a) { + const uint8_t* p; + char signature[sizeof(rar5_signature_xor)]; + + rar5_signature(signature); + + if(!read_ahead(a, sizeof(rar5_signature_xor), &p)) + return -1; + + if(!memcmp(signature, p, sizeof(rar5_signature_xor))) + return 30; + + return -1; +} + +static int rar5_bid(struct archive_read* a, int best_bid) { + int my_bid; + + if(best_bid > 30) + return -1; + + my_bid = bid_standard(a); + if(my_bid > -1) { + return my_bid; + } + + return -1; +} + +static int rar5_options(struct archive_read *a, const char *key, + const char *val) { + (void) a; + (void) key; + (void) val; + + /* No options supported in this version. Return the ARCHIVE_WARN code + * to signal the options supervisor that the unpacker didn't handle + * setting this option. */ + + return ARCHIVE_WARN; +} + +static void init_header(struct archive_read* a) { + a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5; + a->archive.archive_format_name = "RAR5"; +} + +static void init_window_mask(struct rar5* rar) { + if (rar->cstate.window_size) + rar->cstate.window_mask = rar->cstate.window_size - 1; + else + rar->cstate.window_mask = 0; +} + +enum HEADER_FLAGS { + HFL_EXTRA_DATA = 0x0001, + HFL_DATA = 0x0002, + HFL_SKIP_IF_UNKNOWN = 0x0004, + HFL_SPLIT_BEFORE = 0x0008, + HFL_SPLIT_AFTER = 0x0010, + HFL_CHILD = 0x0020, + HFL_INHERITED = 0x0040 +}; + +static int process_main_locator_extra_block(struct archive_read* a, + struct rar5* rar) +{ + uint64_t locator_flags; + + enum LOCATOR_FLAGS { + QLIST = 0x01, RECOVERY = 0x02, + }; + + if(!read_var(a, &locator_flags, NULL)) { + return ARCHIVE_EOF; + } + + if(locator_flags & QLIST) { + if(!read_var(a, &rar->qlist_offset, NULL)) { + return ARCHIVE_EOF; + } + + /* qlist is not used */ + } + + if(locator_flags & RECOVERY) { + if(!read_var(a, &rar->rr_offset, NULL)) { + return ARCHIVE_EOF; + } + + /* rr is not used */ + } + + return ARCHIVE_OK; +} + +static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, + ssize_t* extra_data_size) +{ + size_t hash_type = 0; + size_t value_len; + + enum HASH_TYPE { + BLAKE2sp = 0x00 + }; + + if(!read_var_sized(a, &hash_type, &value_len)) + return ARCHIVE_EOF; + + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) { + return ARCHIVE_EOF; + } + + /* The file uses BLAKE2sp checksum algorithm instead of plain old + * CRC32. */ + if(hash_type == BLAKE2sp) { + const uint8_t* p; + const int hash_size = sizeof(rar->file.blake2sp); + + if(!read_ahead(a, hash_size, &p)) + return ARCHIVE_EOF; + + rar->file.has_blake2 = 1; + memcpy(&rar->file.blake2sp, p, hash_size); + + if(ARCHIVE_OK != consume(a, hash_size)) { + return ARCHIVE_EOF; + } + + *extra_data_size -= hash_size; + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported hash type (0x%x)", (int) hash_type); + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; +} + +static uint64_t time_win_to_unix(uint64_t win_time) { + const size_t ns_in_sec = 10000000; + const uint64_t sec_to_unix = 11644473600LL; + return win_time / ns_in_sec - sec_to_unix; +} + +static int parse_htime_item(struct archive_read* a, char unix_time, + uint64_t* where, ssize_t* extra_data_size) +{ + if(unix_time) { + uint32_t time_val; + if(!read_u32(a, &time_val)) + return ARCHIVE_EOF; + + *extra_data_size -= 4; + *where = (uint64_t) time_val; + } else { + uint64_t windows_time; + if(!read_u64(a, &windows_time)) + return ARCHIVE_EOF; + + *where = time_win_to_unix(windows_time); + *extra_data_size -= 8; + } + + return ARCHIVE_OK; +} + +static int parse_file_extra_version(struct archive_read* a, + struct archive_entry* e, ssize_t* extra_data_size) +{ + size_t flags = 0; + size_t version = 0; + size_t value_len = 0; + struct archive_string version_string; + struct archive_string name_utf8_string; + const char* cur_filename; + + /* Flags are ignored. */ + if(!read_var_sized(a, &flags, &value_len)) + return ARCHIVE_EOF; + + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + + if(!read_var_sized(a, &version, &value_len)) + return ARCHIVE_EOF; + + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + + /* extra_data_size should be zero here. */ + + cur_filename = archive_entry_pathname_utf8(e); + if(cur_filename == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Version entry without file name"); + return ARCHIVE_FATAL; + } + + archive_string_init(&version_string); + archive_string_init(&name_utf8_string); + + /* Prepare a ;123 suffix for the filename, where '123' is the version + * value of this file. */ + archive_string_sprintf(&version_string, ";%zu", version); + + /* Build the new filename. */ + archive_strcat(&name_utf8_string, cur_filename); + archive_strcat(&name_utf8_string, version_string.s); + + /* Apply the new filename into this file's context. */ + archive_entry_update_pathname_utf8(e, name_utf8_string.s); + + /* Free buffers. */ + archive_string_free(&version_string); + archive_string_free(&name_utf8_string); + return ARCHIVE_OK; +} + +static int parse_file_extra_htime(struct archive_read* a, + struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) +{ + char unix_time = 0; + size_t flags = 0; + size_t value_len; + + enum HTIME_FLAGS { + IS_UNIX = 0x01, + HAS_MTIME = 0x02, + HAS_CTIME = 0x04, + HAS_ATIME = 0x08, + HAS_UNIX_NS = 0x10, + }; + + if(!read_var_sized(a, &flags, &value_len)) + return ARCHIVE_EOF; + + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) { + return ARCHIVE_EOF; + } + + unix_time = flags & IS_UNIX; + + if(flags & HAS_MTIME) { + parse_htime_item(a, unix_time, &rar->file.e_mtime, + extra_data_size); + archive_entry_set_mtime(e, rar->file.e_mtime, 0); + } + + if(flags & HAS_CTIME) { + parse_htime_item(a, unix_time, &rar->file.e_ctime, + extra_data_size); + archive_entry_set_ctime(e, rar->file.e_ctime, 0); + } + + if(flags & HAS_ATIME) { + parse_htime_item(a, unix_time, &rar->file.e_atime, + extra_data_size); + archive_entry_set_atime(e, rar->file.e_atime, 0); + } + + if(flags & HAS_UNIX_NS) { + if(!read_u32(a, &rar->file.e_unix_ns)) + return ARCHIVE_EOF; + + *extra_data_size -= 4; + } + + return ARCHIVE_OK; +} + +static int parse_file_extra_redir(struct archive_read* a, + struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) +{ + uint64_t value_size = 0; + size_t target_size = 0; + char target_utf8_buf[MAX_NAME_IN_BYTES]; + const uint8_t* p; + + if(!read_var(a, &rar->file.redir_type, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + if(!read_var(a, &rar->file.redir_flags, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + if(!read_var_sized(a, &target_size, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= target_size + 1; + + if(!read_ahead(a, target_size, &p)) + return ARCHIVE_EOF; + + if(target_size > (MAX_NAME_IN_CHARS - 1)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Link target is too long"); + return ARCHIVE_FATAL; + } + + if(target_size == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "No link target specified"); + return ARCHIVE_FATAL; + } + + memcpy(target_utf8_buf, p, target_size); + target_utf8_buf[target_size] = 0; + + if(ARCHIVE_OK != consume(a, (int64_t)target_size)) + return ARCHIVE_EOF; + + switch(rar->file.redir_type) { + case REDIR_TYPE_UNIXSYMLINK: + case REDIR_TYPE_WINSYMLINK: + archive_entry_set_filetype(e, AE_IFLNK); + archive_entry_update_symlink_utf8(e, target_utf8_buf); + if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) { + archive_entry_set_symlink_type(e, + AE_SYMLINK_TYPE_DIRECTORY); + } else { + archive_entry_set_symlink_type(e, + AE_SYMLINK_TYPE_FILE); + } + break; + + case REDIR_TYPE_HARDLINK: + archive_entry_set_filetype(e, AE_IFREG); + archive_entry_update_hardlink_utf8(e, target_utf8_buf); + break; + + default: + /* Unknown redir type, skip it. */ + break; + } + return ARCHIVE_OK; +} + +static int parse_file_extra_owner(struct archive_read* a, + struct archive_entry* e, ssize_t* extra_data_size) +{ + uint64_t flags = 0; + uint64_t value_size = 0; + uint64_t id = 0; + size_t name_len = 0; + size_t name_size = 0; + char namebuf[OWNER_MAXNAMELEN]; + const uint8_t* p; + + if(!read_var(a, &flags, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + if ((flags & OWNER_USER_NAME) != 0) { + if(!read_var_sized(a, &name_size, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= name_size + 1; + + if(!read_ahead(a, name_size, &p)) + return ARCHIVE_EOF; + + if (name_size >= OWNER_MAXNAMELEN) { + name_len = OWNER_MAXNAMELEN - 1; + } else { + name_len = name_size; + } + + memcpy(namebuf, p, name_len); + namebuf[name_len] = 0; + if(ARCHIVE_OK != consume(a, (int64_t)name_size)) + return ARCHIVE_EOF; + + archive_entry_set_uname(e, namebuf); + } + if ((flags & OWNER_GROUP_NAME) != 0) { + if(!read_var_sized(a, &name_size, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= name_size + 1; + + if(!read_ahead(a, name_size, &p)) + return ARCHIVE_EOF; + + if (name_size >= OWNER_MAXNAMELEN) { + name_len = OWNER_MAXNAMELEN - 1; + } else { + name_len = name_size; + } + + memcpy(namebuf, p, name_len); + namebuf[name_len] = 0; + if(ARCHIVE_OK != consume(a, (int64_t)name_size)) + return ARCHIVE_EOF; + + archive_entry_set_gname(e, namebuf); + } + if ((flags & OWNER_USER_UID) != 0) { + if(!read_var(a, &id, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + archive_entry_set_uid(e, (la_int64_t)id); + } + if ((flags & OWNER_GROUP_GID) != 0) { + if(!read_var(a, &id, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + archive_entry_set_gid(e, (la_int64_t)id); + } + return ARCHIVE_OK; +} + +static int process_head_file_extra(struct archive_read* a, + struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size) +{ + size_t extra_field_size; + size_t extra_field_id = 0; + int ret = ARCHIVE_FATAL; + size_t var_size; + + while(extra_data_size > 0) { + if(!read_var_sized(a, &extra_field_size, &var_size)) + return ARCHIVE_EOF; + + extra_data_size -= var_size; + if(ARCHIVE_OK != consume(a, var_size)) { + return ARCHIVE_EOF; + } + + if(!read_var_sized(a, &extra_field_id, &var_size)) + return ARCHIVE_EOF; + + extra_data_size -= var_size; + if(ARCHIVE_OK != consume(a, var_size)) { + return ARCHIVE_EOF; + } + + switch(extra_field_id) { + case EX_HASH: + ret = parse_file_extra_hash(a, rar, + &extra_data_size); + break; + case EX_HTIME: + ret = parse_file_extra_htime(a, e, rar, + &extra_data_size); + break; + case EX_REDIR: + ret = parse_file_extra_redir(a, e, rar, + &extra_data_size); + break; + case EX_UOWNER: + ret = parse_file_extra_owner(a, e, + &extra_data_size); + break; + case EX_VERSION: + ret = parse_file_extra_version(a, e, + &extra_data_size); + break; + case EX_CRYPT: + /* fallthrough */ + case EX_SUBDATA: + /* fallthrough */ + default: + /* Skip unsupported entry. */ + return consume(a, extra_data_size); + } + } + + if(ret != ARCHIVE_OK) { + /* Attribute not implemented. */ + return ret; + } + + return ARCHIVE_OK; +} + +static int process_head_file(struct archive_read* a, struct rar5* rar, + struct archive_entry* entry, size_t block_flags) +{ + ssize_t extra_data_size = 0; + size_t data_size = 0; + size_t file_flags = 0; + size_t file_attr = 0; + size_t compression_info = 0; + size_t host_os = 0; + size_t name_size = 0; + uint64_t unpacked_size, window_size; + uint32_t mtime = 0, crc = 0; + int c_method = 0, c_version = 0; + char name_utf8_buf[MAX_NAME_IN_BYTES]; + const uint8_t* p; + + enum FILE_FLAGS { + DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004, + UNKNOWN_UNPACKED_SIZE = 0x0008, + }; + + enum FILE_ATTRS { + ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4, + ATTR_DIRECTORY = 0x10, + }; + + enum COMP_INFO_FLAGS { + SOLID = 0x0040, + }; + + enum HOST_OS { + HOST_WINDOWS = 0, + HOST_UNIX = 1, + }; + + archive_entry_clear(entry); + + /* Do not reset file context if we're switching archives. */ + if(!rar->cstate.switch_multivolume) { + reset_file_context(rar); + } + + if(block_flags & HFL_EXTRA_DATA) { + size_t edata_size = 0; + if(!read_var_sized(a, &edata_size, NULL)) + return ARCHIVE_EOF; + + /* Intentional type cast from unsigned to signed. */ + extra_data_size = (ssize_t) edata_size; + } + + if(block_flags & HFL_DATA) { + if(!read_var_sized(a, &data_size, NULL)) + return ARCHIVE_EOF; + + rar->file.bytes_remaining = data_size; + } else { + rar->file.bytes_remaining = 0; + + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "no data found in file/service block"); + return ARCHIVE_FATAL; + } + + if(!read_var_sized(a, &file_flags, NULL)) + return ARCHIVE_EOF; + + if(!read_var(a, &unpacked_size, NULL)) + return ARCHIVE_EOF; + + if(file_flags & UNKNOWN_UNPACKED_SIZE) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Files with unknown unpacked size are not supported"); + return ARCHIVE_FATAL; + } + + rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0); + + if(!read_var_sized(a, &file_attr, NULL)) + return ARCHIVE_EOF; + + if(file_flags & UTIME) { + if(!read_u32(a, &mtime)) + return ARCHIVE_EOF; + } + + if(file_flags & CRC32) { + if(!read_u32(a, &crc)) + return ARCHIVE_EOF; + } + + if(!read_var_sized(a, &compression_info, NULL)) + return ARCHIVE_EOF; + + c_method = (int) (compression_info >> 7) & 0x7; + c_version = (int) (compression_info & 0x3f); + + /* RAR5 seems to limit the dictionary size to 64MB. */ + window_size = (rar->file.dir > 0) ? + 0 : + g_unpack_window_size << ((compression_info >> 10) & 15); + rar->cstate.method = c_method; + rar->cstate.version = c_version + 50; + rar->file.solid = (compression_info & SOLID) > 0; + + /* Archives which declare solid files without initializing the window + * buffer first are invalid. */ + + if(rar->file.solid > 0 && rar->cstate.window_buf == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Declared solid file, but no window buffer " + "initialized yet."); + return ARCHIVE_FATAL; + } + + /* Check if window_size is a sane value. Also, if the file is not + * declared as a directory, disallow window_size == 0. */ + if(window_size > (64 * 1024 * 1024) || + (rar->file.dir == 0 && window_size == 0)) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Declared dictionary size is not supported."); + return ARCHIVE_FATAL; + } + + if(rar->file.solid > 0) { + /* Re-check if current window size is the same as previous + * window size (for solid files only). */ + if(rar->file.solid_window_size > 0 && + rar->file.solid_window_size != (ssize_t) window_size) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Window size for this solid file doesn't match " + "the window size used in previous solid file. "); + return ARCHIVE_FATAL; + } + } + + /* If we're currently switching volumes, ignore the new definition of + * window_size. */ + if(rar->cstate.switch_multivolume == 0) { + /* Values up to 64M should fit into ssize_t on every + * architecture. */ + rar->cstate.window_size = (ssize_t) window_size; + } + + if(rar->file.solid > 0 && rar->file.solid_window_size == 0) { + /* Solid files have to have the same window_size across + whole archive. Remember the window_size parameter + for first solid file found. */ + rar->file.solid_window_size = rar->cstate.window_size; + } + + init_window_mask(rar); + + rar->file.service = 0; + + if(!read_var_sized(a, &host_os, NULL)) + return ARCHIVE_EOF; + + if(host_os == HOST_WINDOWS) { + /* Host OS is Windows */ + + __LA_MODE_T mode; + + if(file_attr & ATTR_DIRECTORY) { + if (file_attr & ATTR_READONLY) { + mode = 0555 | AE_IFDIR; + } else { + mode = 0755 | AE_IFDIR; + } + } else { + if (file_attr & ATTR_READONLY) { + mode = 0444 | AE_IFREG; + } else { + mode = 0644 | AE_IFREG; + } + } + + archive_entry_set_mode(entry, mode); + + if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) { + char *fflags_text, *ptr; + /* allocate for "rdonly,hidden,system," */ + fflags_text = malloc(22 * sizeof(char)); + if (fflags_text != NULL) { + ptr = fflags_text; + if (file_attr & ATTR_READONLY) { + strcpy(ptr, "rdonly,"); + ptr = ptr + 7; + } + if (file_attr & ATTR_HIDDEN) { + strcpy(ptr, "hidden,"); + ptr = ptr + 7; + } + if (file_attr & ATTR_SYSTEM) { + strcpy(ptr, "system,"); + ptr = ptr + 7; + } + if (ptr > fflags_text) { + /* Delete trailing comma */ + *(ptr - 1) = '\0'; + archive_entry_copy_fflags_text(entry, + fflags_text); + } + free(fflags_text); + } + } + } else if(host_os == HOST_UNIX) { + /* Host OS is Unix */ + archive_entry_set_mode(entry, (__LA_MODE_T) file_attr); + } else { + /* Unknown host OS */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported Host OS: 0x%x", (int) host_os); + + return ARCHIVE_FATAL; + } + + if(!read_var_sized(a, &name_size, NULL)) + return ARCHIVE_EOF; + + if(!read_ahead(a, name_size, &p)) + return ARCHIVE_EOF; + + if(name_size > (MAX_NAME_IN_CHARS - 1)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Filename is too long"); + + return ARCHIVE_FATAL; + } + + if(name_size == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "No filename specified"); + + return ARCHIVE_FATAL; + } + + memcpy(name_utf8_buf, p, name_size); + name_utf8_buf[name_size] = 0; + if(ARCHIVE_OK != consume(a, name_size)) { + return ARCHIVE_EOF; + } + + archive_entry_update_pathname_utf8(entry, name_utf8_buf); + + if(extra_data_size > 0) { + int ret = process_head_file_extra(a, entry, rar, + extra_data_size); + + /* + * TODO: rewrite or remove useless sanity check + * as extra_data_size is not passed as a pointer + * + if(extra_data_size < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "File extra data size is not zero"); + return ARCHIVE_FATAL; + } + */ + + if(ret != ARCHIVE_OK) + return ret; + } + + if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) { + rar->file.unpacked_size = (ssize_t) unpacked_size; + if(rar->file.redir_type == REDIR_TYPE_NONE) + archive_entry_set_size(entry, unpacked_size); + } + + if(file_flags & UTIME) { + archive_entry_set_mtime(entry, (time_t) mtime, 0); + } + + if(file_flags & CRC32) { + rar->file.stored_crc32 = crc; + } + + if(!rar->cstate.switch_multivolume) { + /* Do not reinitialize unpacking state if we're switching + * archives. */ + rar->cstate.block_parsing_finished = 1; + rar->cstate.all_filters_applied = 1; + rar->cstate.initialized = 0; + } + + if(rar->generic.split_before > 0) { + /* If now we're standing on a header that has a 'split before' + * mark, it means we're standing on a 'continuation' file + * header. Signal the caller that if it wants to move to + * another file, it must call rar5_read_header() function + * again. */ + + return ARCHIVE_RETRY; + } else { + return ARCHIVE_OK; + } +} + +static int process_head_service(struct archive_read* a, struct rar5* rar, + struct archive_entry* entry, size_t block_flags) +{ + /* Process this SERVICE block the same way as FILE blocks. */ + int ret = process_head_file(a, rar, entry, block_flags); + if(ret != ARCHIVE_OK) + return ret; + + rar->file.service = 1; + + /* But skip the data part automatically. It's no use for the user + * anyway. It contains only service data, not even needed to + * properly unpack the file. */ + ret = rar5_read_data_skip(a); + if(ret != ARCHIVE_OK) + return ret; + + /* After skipping, try parsing another block automatically. */ + return ARCHIVE_RETRY; +} + +static int process_head_main(struct archive_read* a, struct rar5* rar, + struct archive_entry* entry, size_t block_flags) +{ + int ret; + size_t extra_data_size = 0; + size_t extra_field_size = 0; + size_t extra_field_id = 0; + size_t archive_flags = 0; + + enum MAIN_FLAGS { + VOLUME = 0x0001, /* multi-volume archive */ + VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't + * have it */ + SOLID = 0x0004, /* solid archive */ + PROTECT = 0x0008, /* contains Recovery info */ + LOCK = 0x0010, /* readonly flag, not used */ + }; + + enum MAIN_EXTRA { + // Just one attribute here. + LOCATOR = 0x01, + }; + + (void) entry; + + if(block_flags & HFL_EXTRA_DATA) { + if(!read_var_sized(a, &extra_data_size, NULL)) + return ARCHIVE_EOF; + } else { + extra_data_size = 0; + } + + if(!read_var_sized(a, &archive_flags, NULL)) { + return ARCHIVE_EOF; + } + + rar->main.volume = (archive_flags & VOLUME) > 0; + rar->main.solid = (archive_flags & SOLID) > 0; + + if(archive_flags & VOLUME_NUMBER) { + size_t v = 0; + if(!read_var_sized(a, &v, NULL)) { + return ARCHIVE_EOF; + } + + if (v > UINT_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid volume number"); + return ARCHIVE_FATAL; + } + + rar->main.vol_no = (unsigned int) v; + } else { + rar->main.vol_no = 0; + } + + if(rar->vol.expected_vol_no > 0 && + rar->main.vol_no != rar->vol.expected_vol_no) + { + /* Returning EOF instead of FATAL because of strange + * libarchive behavior. When opening multiple files via + * archive_read_open_filenames(), after reading up the whole + * last file, the __archive_read_ahead function wraps up to + * the first archive instead of returning EOF. */ + return ARCHIVE_EOF; + } + + if(extra_data_size == 0) { + /* Early return. */ + return ARCHIVE_OK; + } + + if(!read_var_sized(a, &extra_field_size, NULL)) { + return ARCHIVE_EOF; + } + + if(!read_var_sized(a, &extra_field_id, NULL)) { + return ARCHIVE_EOF; + } + + if(extra_field_size == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid extra field size"); + return ARCHIVE_FATAL; + } + + switch(extra_field_id) { + case LOCATOR: + ret = process_main_locator_extra_block(a, rar); + if(ret != ARCHIVE_OK) { + /* Error while parsing main locator extra + * block. */ + return ret; + } + + break; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported extra type (0x%x)", + (int) extra_field_id); + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; +} + +static int skip_unprocessed_bytes(struct archive_read* a) { + struct rar5* rar = get_context(a); + int ret; + + if(rar->file.bytes_remaining) { + /* Use different skipping method in block merging mode than in + * normal mode. If merge mode is active, rar5_read_data_skip + * can't be used, because it could allow recursive use of + * merge_block() * function, and this function doesn't support + * recursive use. */ + if(rar->merge_mode) { + /* Discard whole merged block. This is valid in solid + * mode as well, because the code will discard blocks + * only if those blocks are safe to discard (i.e. + * they're not FILE blocks). */ + ret = consume(a, rar->file.bytes_remaining); + if(ret != ARCHIVE_OK) { + return ret; + } + rar->file.bytes_remaining = 0; + } else { + /* If we're not in merge mode, use safe skipping code. + * This will ensure we'll handle solid archives + * properly. */ + ret = rar5_read_data_skip(a); + if(ret != ARCHIVE_OK) { + return ret; + } + } + } + + return ARCHIVE_OK; +} + +static int scan_for_signature(struct archive_read* a); + +/* Base block processing function. A 'base block' is a RARv5 header block + * that tells the reader what kind of data is stored inside the block. + * + * From the birds-eye view a RAR file looks file this: + * + * ... + * + * There are a few types of base blocks. Those types are specified inside + * the 'switch' statement in this function. For example purposes, I'll write + * how a standard RARv5 file could look like here: + * + *
+ * + * The structure above could describe an archive file with 3 files in it, + * one service "QuickOpen" block (that is ignored by this parser), and an + * end of file base block marker. + * + * If the file is stored in multiple archive files ("multiarchive"), it might + * look like this: + * + * .part01.rar:
+ * .part02.rar:
+ * .part03.rar:
+ * + * This example could describe 3 RAR files that contain ONE archived file. + * Or it could describe 3 RAR files that contain 3 different files. Or 3 + * RAR files than contain 2 files. It all depends what metadata is stored in + * the headers of blocks. + * + * Each block contains info about its size, the name of the file it's + * storing inside, and whether this FILE block is a continuation block of + * previous archive ('split before'), and is this FILE block should be + * continued in another archive ('split after'). By parsing the 'split before' + * and 'split after' flags, we're able to tell if multiple base blocks + * are describing one file, or multiple files (with the same filename, for + * example). + * + * One thing to note is that if we're parsing the first block, and + * we see 'split after' flag, then we need to jump over to another + * block to be able to decompress rest of the data. To do this, we need + * to skip the block, then switch to another file, then skip the + * block,
block, and then we're standing on the proper + * block. + */ + +static int process_base_block(struct archive_read* a, + struct archive_entry* entry) +{ + const size_t SMALLEST_RAR5_BLOCK_SIZE = 3; + + struct rar5* rar = get_context(a); + uint32_t hdr_crc, computed_crc; + size_t raw_hdr_size = 0, hdr_size_len, hdr_size; + size_t header_id = 0; + size_t header_flags = 0; + const uint8_t* p; + int ret; + + enum HEADER_TYPE { + HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02, + HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05, + HEAD_UNKNOWN = 0xff, + }; + + /* Skip any unprocessed data for this file. */ + ret = skip_unprocessed_bytes(a); + if(ret != ARCHIVE_OK) + return ret; + + /* Read the expected CRC32 checksum. */ + if(!read_u32(a, &hdr_crc)) { + return ARCHIVE_EOF; + } + + /* Read header size. */ + if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) { + return ARCHIVE_EOF; + } + + hdr_size = raw_hdr_size + hdr_size_len; + + /* Sanity check, maximum header size for RAR5 is 2MB. */ + if(hdr_size > (2 * 1024 * 1024)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Base block header is too large"); + + return ARCHIVE_FATAL; + } + + /* Additional sanity checks to weed out invalid files. */ + if(raw_hdr_size == 0 || hdr_size_len == 0 || + hdr_size < SMALLEST_RAR5_BLOCK_SIZE) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Too small block encountered (%zu bytes)", + raw_hdr_size); + + return ARCHIVE_FATAL; + } + + /* Read the whole header data into memory, maximum memory use here is + * 2MB. */ + if(!read_ahead(a, hdr_size, &p)) { + return ARCHIVE_EOF; + } + + /* Verify the CRC32 of the header data. */ + computed_crc = (uint32_t) crc32(0, p, (int) hdr_size); + if(computed_crc != hdr_crc) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Header CRC error"); + + return ARCHIVE_FATAL; + } + + /* If the checksum is OK, we proceed with parsing. */ + if(ARCHIVE_OK != consume(a, hdr_size_len)) { + return ARCHIVE_EOF; + } + + if(!read_var_sized(a, &header_id, NULL)) + return ARCHIVE_EOF; + + if(!read_var_sized(a, &header_flags, NULL)) + return ARCHIVE_EOF; + + rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; + rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; + rar->generic.size = (int)hdr_size; + rar->generic.last_header_id = (int)header_id; + rar->main.endarc = 0; + + /* Those are possible header ids in RARv5. */ + switch(header_id) { + case HEAD_MAIN: + ret = process_head_main(a, rar, entry, header_flags); + + /* Main header doesn't have any files in it, so it's + * pointless to return to the caller. Retry to next + * header, which should be HEAD_FILE/HEAD_SERVICE. */ + if(ret == ARCHIVE_OK) + return ARCHIVE_RETRY; + + return ret; + case HEAD_SERVICE: + ret = process_head_service(a, rar, entry, header_flags); + return ret; + case HEAD_FILE: + ret = process_head_file(a, rar, entry, header_flags); + return ret; + case HEAD_CRYPT: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Encryption is not supported"); + return ARCHIVE_FATAL; + case HEAD_ENDARC: + rar->main.endarc = 1; + + /* After encountering an end of file marker, we need + * to take into consideration if this archive is + * continued in another file (i.e. is it part01.rar: + * is there a part02.rar?) */ + if(rar->main.volume) { + /* In case there is part02.rar, position the + * read pointer in a proper place, so we can + * resume parsing. */ + ret = scan_for_signature(a); + if(ret == ARCHIVE_FATAL) { + return ARCHIVE_EOF; + } else { + if(rar->vol.expected_vol_no == + UINT_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Header error"); + return ARCHIVE_FATAL; + } + + rar->vol.expected_vol_no = + rar->main.vol_no + 1; + return ARCHIVE_OK; + } + } else { + return ARCHIVE_EOF; + } + case HEAD_MARK: + return ARCHIVE_EOF; + default: + if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Header type error"); + return ARCHIVE_FATAL; + } else { + /* If the block is marked as 'skip if unknown', + * do as the flag says: skip the block + * instead on failing on it. */ + return ARCHIVE_RETRY; + } + } + +#if !defined WIN32 + // Not reached. + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Internal unpacker error"); + return ARCHIVE_FATAL; +#endif +} + +static int skip_base_block(struct archive_read* a) { + int ret; + struct rar5* rar = get_context(a); + + /* Create a new local archive_entry structure that will be operated on + * by header reader; operations on this archive_entry will be discarded. + */ + struct archive_entry* entry = archive_entry_new(); + ret = process_base_block(a, entry); + + /* Discard operations on this archive_entry structure. */ + archive_entry_free(entry); + if(ret == ARCHIVE_FATAL) + return ret; + + if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0) + return ARCHIVE_OK; + + if(ret == ARCHIVE_OK) + return ARCHIVE_RETRY; + else + return ret; +} + +static int rar5_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + struct rar5* rar = get_context(a); + int ret; + + if(rar->header_initialized == 0) { + init_header(a); + rar->header_initialized = 1; + } + + if(rar->skipped_magic == 0) { + if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) { + return ARCHIVE_EOF; + } + + rar->skipped_magic = 1; + } + + do { + ret = process_base_block(a, entry); + } while(ret == ARCHIVE_RETRY || + (rar->main.endarc > 0 && ret == ARCHIVE_OK)); + + return ret; +} + +static void init_unpack(struct rar5* rar) { + rar->file.calculated_crc32 = 0; + init_window_mask(rar); + + free(rar->cstate.window_buf); + free(rar->cstate.filtered_buf); + + if(rar->cstate.window_size > 0) { + rar->cstate.window_buf = calloc(1, rar->cstate.window_size); + rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size); + } else { + rar->cstate.window_buf = NULL; + rar->cstate.filtered_buf = NULL; + } + + rar->cstate.write_ptr = 0; + rar->cstate.last_write_ptr = 0; + + memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd)); + memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld)); + memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd)); + memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd)); + memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd)); +} + +static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { + int verify_crc; + + if(rar->skip_mode) { +#if defined CHECK_CRC_ON_SOLID_SKIP + verify_crc = 1; +#else + verify_crc = 0; +#endif + } else + verify_crc = 1; + + if(verify_crc) { + /* Don't update CRC32 if the file doesn't have the + * `stored_crc32` info filled in. */ + if(rar->file.stored_crc32 > 0) { + rar->file.calculated_crc32 = + crc32(rar->file.calculated_crc32, p, to_read); + } + + /* Check if the file uses an optional BLAKE2sp checksum + * algorithm. */ + if(rar->file.has_blake2 > 0) { + /* Return value of the `update` function is always 0, + * so we can explicitly ignore it here. */ + (void) blake2sp_update(&rar->file.b2state, p, to_read); + } + } +} + +static int create_decode_tables(uint8_t* bit_length, + struct decode_table* table, int size) +{ + int code, upper_limit = 0, i, lc[16]; + uint32_t decode_pos_clone[rar5_countof(table->decode_pos)]; + ssize_t cur_len, quick_data_size; + + memset(&lc, 0, sizeof(lc)); + memset(table->decode_num, 0, sizeof(table->decode_num)); + table->size = size; + table->quick_bits = size == HUFF_NC ? 10 : 7; + + for(i = 0; i < size; i++) { + lc[bit_length[i] & 15]++; + } + + lc[0] = 0; + table->decode_pos[0] = 0; + table->decode_len[0] = 0; + + for(i = 1; i < 16; i++) { + upper_limit += lc[i]; + + table->decode_len[i] = upper_limit << (16 - i); + table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1]; + + upper_limit <<= 1; + } + + memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone)); + + for(i = 0; i < size; i++) { + uint8_t clen = bit_length[i] & 15; + if(clen > 0) { + int last_pos = decode_pos_clone[clen]; + table->decode_num[last_pos] = i; + decode_pos_clone[clen]++; + } + } + + quick_data_size = (int64_t)1 << table->quick_bits; + cur_len = 1; + for(code = 0; code < quick_data_size; code++) { + int bit_field = code << (16 - table->quick_bits); + int dist, pos; + + while(cur_len < rar5_countof(table->decode_len) && + bit_field >= table->decode_len[cur_len]) { + cur_len++; + } + + table->quick_len[code] = (uint8_t) cur_len; + + dist = bit_field - table->decode_len[cur_len - 1]; + dist >>= (16 - cur_len); + + pos = table->decode_pos[cur_len & 15] + dist; + if(cur_len < rar5_countof(table->decode_pos) && pos < size) { + table->quick_num[code] = table->decode_num[pos]; + } else { + table->quick_num[code] = 0; + } + } + + return ARCHIVE_OK; +} + +static int decode_number(struct archive_read* a, struct decode_table* table, + const uint8_t* p, uint16_t* num) +{ + int i, bits, dist; + uint16_t bitfield; + uint32_t pos; + struct rar5* rar = get_context(a); + + if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) { + return ARCHIVE_EOF; + } + + bitfield &= 0xfffe; + + if(bitfield < table->decode_len[table->quick_bits]) { + int code = bitfield >> (16 - table->quick_bits); + skip_bits(rar, table->quick_len[code]); + *num = table->quick_num[code]; + return ARCHIVE_OK; + } + + bits = 15; + + for(i = table->quick_bits + 1; i < 15; i++) { + if(bitfield < table->decode_len[i]) { + bits = i; + break; + } + } + + skip_bits(rar, bits); + + dist = bitfield - table->decode_len[bits - 1]; + dist >>= (16 - bits); + pos = table->decode_pos[bits] + dist; + + if(pos >= table->size) + pos = 0; + + *num = table->decode_num[pos]; + return ARCHIVE_OK; +} + +/* Reads and parses Huffman tables from the beginning of the block. */ +static int parse_tables(struct archive_read* a, struct rar5* rar, + const uint8_t* p) +{ + int ret, value, i, w, idx = 0; + uint8_t bit_length[HUFF_BC], + table[HUFF_TABLE_SIZE], + nibble_mask = 0xF0, + nibble_shift = 4; + + enum { ESCAPE = 15 }; + + /* The data for table generation is compressed using a simple RLE-like + * algorithm when storing zeroes, so we need to unpack it first. */ + for(w = 0, i = 0; w < HUFF_BC;) { + if(i >= rar->cstate.cur_block_size) { + /* Truncated data, can't continue. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated data in huffman tables"); + return ARCHIVE_FATAL; + } + + value = (p[i] & nibble_mask) >> nibble_shift; + + if(nibble_mask == 0x0F) + ++i; + + nibble_mask ^= 0xFF; + nibble_shift ^= 4; + + /* Values smaller than 15 is data, so we write it directly. + * Value 15 is a flag telling us that we need to unpack more + * bytes. */ + if(value == ESCAPE) { + value = (p[i] & nibble_mask) >> nibble_shift; + if(nibble_mask == 0x0F) + ++i; + nibble_mask ^= 0xFF; + nibble_shift ^= 4; + + if(value == 0) { + /* We sometimes need to write the actual value + * of 15, so this case handles that. */ + bit_length[w++] = ESCAPE; + } else { + int k; + + /* Fill zeroes. */ + for(k = 0; (k < value + 2) && (w < HUFF_BC); + k++) { + bit_length[w++] = 0; + } + } + } else { + bit_length[w++] = value; + } + } + + rar->bits.in_addr = i; + rar->bits.bit_addr = nibble_shift ^ 4; + + ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Decoding huffman tables failed"); + return ARCHIVE_FATAL; + } + + for(i = 0; i < HUFF_TABLE_SIZE;) { + uint16_t num; + + if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) { + /* Truncated data, can't continue. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated data in huffman tables (#2)"); + return ARCHIVE_FATAL; + } + + ret = decode_number(a, &rar->cstate.bd, p, &num); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Decoding huffman tables failed"); + return ARCHIVE_FATAL; + } + + if(num < 16) { + /* 0..15: store directly */ + table[i] = (uint8_t) num; + i++; + } else if(num < 18) { + /* 16..17: repeat previous code */ + uint16_t n; + + if(ARCHIVE_OK != read_bits_16(rar, p, &n)) + return ARCHIVE_EOF; + + if(num == 16) { + n >>= 13; + n += 3; + skip_bits(rar, 3); + } else { + n >>= 9; + n += 11; + skip_bits(rar, 7); + } + + if(i > 0) { + while(n-- > 0 && i < HUFF_TABLE_SIZE) { + table[i] = table[i - 1]; + i++; + } + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unexpected error when decoding " + "huffman tables"); + return ARCHIVE_FATAL; + } + } else { + /* other codes: fill with zeroes `n` times */ + uint16_t n; + + if(ARCHIVE_OK != read_bits_16(rar, p, &n)) + return ARCHIVE_EOF; + + if(num == 18) { + n >>= 13; + n += 3; + skip_bits(rar, 3); + } else { + n >>= 9; + n += 11; + skip_bits(rar, 7); + } + + while(n-- > 0 && i < HUFF_TABLE_SIZE) + table[i++] = 0; + } + } + + ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Failed to create literal table"); + return ARCHIVE_FATAL; + } + + idx += HUFF_NC; + + ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Failed to create distance table"); + return ARCHIVE_FATAL; + } + + idx += HUFF_DC; + + ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Failed to create lower bits of distances table"); + return ARCHIVE_FATAL; + } + + idx += HUFF_LDC; + + ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Failed to create repeating distances table"); + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; +} + +/* Parses the block header, verifies its CRC byte, and saves the header + * fields inside the `hdr` pointer. */ +static int parse_block_header(struct archive_read* a, const uint8_t* p, + ssize_t* block_size, struct compressed_block_header* hdr) +{ + uint8_t calculated_cksum; + memcpy(hdr, p, sizeof(struct compressed_block_header)); + + if(bf_byte_count(hdr) > 2) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported block header size (was %d, max is 2)", + bf_byte_count(hdr)); + return ARCHIVE_FATAL; + } + + /* This should probably use bit reader interface in order to be more + * future-proof. */ + *block_size = 0; + switch(bf_byte_count(hdr)) { + /* 1-byte block size */ + case 0: + *block_size = *(const uint8_t*) &p[2]; + break; + + /* 2-byte block size */ + case 1: + *block_size = archive_le16dec(&p[2]); + break; + + /* 3-byte block size */ + case 2: + *block_size = archive_le32dec(&p[2]); + *block_size &= 0x00FFFFFF; + break; + + /* Other block sizes are not supported. This case is not + * reached, because we have an 'if' guard before the switch + * that makes sure of it. */ + default: + return ARCHIVE_FATAL; + } + + /* Verify the block header checksum. 0x5A is a magic value and is + * always * constant. */ + calculated_cksum = 0x5A + ^ (uint8_t) hdr->block_flags_u8 + ^ (uint8_t) *block_size + ^ (uint8_t) (*block_size >> 8) + ^ (uint8_t) (*block_size >> 16); + + if(calculated_cksum != hdr->block_cksum) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Block checksum error: got 0x%x, expected 0x%x", + hdr->block_cksum, calculated_cksum); + + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; +} + +/* Convenience function used during filter processing. */ +static int parse_filter_data(struct rar5* rar, const uint8_t* p, + uint32_t* filter_data) +{ + int i, bytes; + uint32_t data = 0; + + if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes)) + return ARCHIVE_EOF; + + bytes++; + + for(i = 0; i < bytes; i++) { + uint16_t byte; + + if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) { + return ARCHIVE_EOF; + } + + /* Cast to uint32_t will ensure the shift operation will not + * produce undefined result. */ + data += ((uint32_t) byte >> 8) << (i * 8); + skip_bits(rar, 8); + } + + *filter_data = data; + return ARCHIVE_OK; +} + +/* Function is used during sanity checking. */ +static int is_valid_filter_block_start(struct rar5* rar, + uint32_t start) +{ + const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr; + const int64_t last_bs = rar->cstate.last_block_start; + const ssize_t last_bl = rar->cstate.last_block_length; + + if(last_bs == 0 || last_bl == 0) { + /* We didn't have any filters yet, so accept this offset. */ + return 1; + } + + if(block_start >= last_bs + last_bl) { + /* Current offset is bigger than last block's end offset, so + * accept current offset. */ + return 1; + } + + /* Any other case is not a normal situation and we should fail. */ + return 0; +} + +/* The function will create a new filter, read its parameters from the input + * stream and add it to the filter collection. */ +static int parse_filter(struct archive_read* ar, const uint8_t* p) { + uint32_t block_start, block_length; + uint16_t filter_type; + struct filter_info* filt = NULL; + struct rar5* rar = get_context(ar); + + /* Read the parameters from the input stream. */ + if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start)) + return ARCHIVE_EOF; + + if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length)) + return ARCHIVE_EOF; + + if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type)) + return ARCHIVE_EOF; + + filter_type >>= 13; + skip_bits(rar, 3); + + /* Perform some sanity checks on this filter parameters. Note that we + * allow only DELTA, E8/E9 and ARM filters here, because rest of + * filters are not used in RARv5. */ + + if(block_length < 4 || + block_length > 0x400000 || + filter_type > FILTER_ARM || + !is_valid_filter_block_start(rar, block_start)) + { + archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid filter encountered"); + return ARCHIVE_FATAL; + } + + /* Allocate a new filter. */ + filt = add_new_filter(rar); + if(filt == NULL) { + archive_set_error(&ar->archive, ENOMEM, + "Can't allocate memory for a filter descriptor."); + return ARCHIVE_FATAL; + } + + filt->type = filter_type; + filt->block_start = rar->cstate.write_ptr + block_start; + filt->block_length = block_length; + + rar->cstate.last_block_start = filt->block_start; + rar->cstate.last_block_length = filt->block_length; + + /* Read some more data in case this is a DELTA filter. Other filter + * types don't require any additional data over what was already + * read. */ + if(filter_type == FILTER_DELTA) { + int channels; + + if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels)) + return ARCHIVE_EOF; + + filt->channels = channels + 1; + } + + return ARCHIVE_OK; +} + +static int decode_code_length(struct rar5* rar, const uint8_t* p, + uint16_t code) +{ + int lbits, length = 2; + if(code < 8) { + lbits = 0; + length += code; + } else { + lbits = code / 4 - 1; + length += (4 | (code & 3)) << lbits; + } + + if(lbits > 0) { + int add; + + if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add)) + return -1; + + length += add; + } + + return length; +} + +static int copy_string(struct archive_read* a, int len, int dist) { + struct rar5* rar = get_context(a); + const uint64_t cmask = rar->cstate.window_mask; + const uint64_t write_ptr = rar->cstate.write_ptr + + rar->cstate.solid_offset; + int i; + + if (rar->cstate.window_buf == NULL) + return ARCHIVE_FATAL; + + /* The unpacker spends most of the time in this function. It would be + * a good idea to introduce some optimizations here. + * + * Just remember that this loop treats buffers that overlap differently + * than buffers that do not overlap. This is why a simple memcpy(3) + * call will not be enough. */ + + for(i = 0; i < len; i++) { + const ssize_t write_idx = (write_ptr + i) & cmask; + const ssize_t read_idx = (write_ptr + i - dist) & cmask; + rar->cstate.window_buf[write_idx] = + rar->cstate.window_buf[read_idx]; + } + + rar->cstate.write_ptr += len; + return ARCHIVE_OK; +} + +static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { + struct rar5* rar = get_context(a); + uint16_t num; + int ret; + + const uint64_t cmask = rar->cstate.window_mask; + const struct compressed_block_header* hdr = &rar->last_block_hdr; + const uint8_t bit_size = 1 + bf_bit_size(hdr); + + while(1) { + if(rar->cstate.write_ptr - rar->cstate.last_write_ptr > + (rar->cstate.window_size >> 1)) { + /* Don't allow growing data by more than half of the + * window size at a time. In such case, break the loop; + * next call to this function will continue processing + * from this moment. */ + break; + } + + if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 || + (rar->bits.in_addr == rar->cstate.cur_block_size - 1 && + rar->bits.bit_addr >= bit_size)) + { + /* If the program counter is here, it means the + * function has finished processing the block. */ + rar->cstate.block_parsing_finished = 1; + break; + } + + /* Decode the next literal. */ + if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) { + return ARCHIVE_EOF; + } + + /* Num holds a decompression literal, or 'command code'. + * + * - Values lower than 256 are just bytes. Those codes + * can be stored in the output buffer directly. + * + * - Code 256 defines a new filter, which is later used to + * ransform the data block accordingly to the filter type. + * The data block needs to be fully uncompressed first. + * + * - Code bigger than 257 and smaller than 262 define + * a repetition pattern that should be copied from + * an already uncompressed chunk of data. + */ + + if(num < 256) { + /* Directly store the byte. */ + int64_t write_idx = rar->cstate.solid_offset + + rar->cstate.write_ptr++; + + rar->cstate.window_buf[write_idx & cmask] = + (uint8_t) num; + continue; + } else if(num >= 262) { + uint16_t dist_slot; + int len = decode_code_length(rar, p, num - 262), + dbits, + dist = 1; + + if(len == -1) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Failed to decode the code length"); + + return ARCHIVE_FATAL; + } + + if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p, + &dist_slot)) + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Failed to decode the distance slot"); + + return ARCHIVE_FATAL; + } + + if(dist_slot < 4) { + dbits = 0; + dist += dist_slot; + } else { + dbits = dist_slot / 2 - 1; + + /* Cast to uint32_t will make sure the shift + * left operation won't produce undefined + * result. Then, the uint32_t type will + * be implicitly casted to int. */ + dist += (uint32_t) (2 | + (dist_slot & 1)) << dbits; + } + + if(dbits > 0) { + if(dbits >= 4) { + uint32_t add = 0; + uint16_t low_dist; + + if(dbits > 4) { + if(ARCHIVE_OK != read_bits_32( + rar, p, &add)) { + /* Return EOF if we + * can't read more + * data. */ + return ARCHIVE_EOF; + } + + skip_bits(rar, dbits - 4); + add = (add >> ( + 36 - dbits)) << 4; + dist += add; + } + + if(ARCHIVE_OK != decode_number(a, + &rar->cstate.ldd, p, &low_dist)) + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Failed to decode the " + "distance slot"); + + return ARCHIVE_FATAL; + } + + if(dist >= INT_MAX - low_dist - 1) { + /* This only happens in + * invalid archives. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Distance pointer " + "overflow"); + return ARCHIVE_FATAL; + } + + dist += low_dist; + } else { + /* dbits is one of [0,1,2,3] */ + int add; + + if(ARCHIVE_OK != read_consume_bits(rar, + p, dbits, &add)) { + /* Return EOF if we can't read + * more data. */ + return ARCHIVE_EOF; + } + + dist += add; + } + } + + if(dist > 0x100) { + len++; + + if(dist > 0x2000) { + len++; + + if(dist > 0x40000) { + len++; + } + } + } + + dist_cache_push(rar, dist); + rar->cstate.last_len = len; + + if(ARCHIVE_OK != copy_string(a, len, dist)) + return ARCHIVE_FATAL; + + continue; + } else if(num == 256) { + /* Create a filter. */ + ret = parse_filter(a, p); + if(ret != ARCHIVE_OK) + return ret; + + continue; + } else if(num == 257) { + if(rar->cstate.last_len != 0) { + if(ARCHIVE_OK != copy_string(a, + rar->cstate.last_len, + rar->cstate.dist_cache[0])) + { + return ARCHIVE_FATAL; + } + } + + continue; + } else { + /* num < 262 */ + const int idx = num - 258; + const int dist = dist_cache_touch(rar, idx); + + uint16_t len_slot; + int len; + + if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p, + &len_slot)) { + return ARCHIVE_FATAL; + } + + len = decode_code_length(rar, p, len_slot); + rar->cstate.last_len = len; + + if(ARCHIVE_OK != copy_string(a, len, dist)) + return ARCHIVE_FATAL; + + continue; + } + + /* The program counter shouldn't reach here. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported block code: 0x%x", num); + + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; +} + +/* Binary search for the RARv5 signature. */ +static int scan_for_signature(struct archive_read* a) { + const uint8_t* p; + const int chunk_size = 512; + ssize_t i; + char signature[sizeof(rar5_signature_xor)]; + + /* If we're here, it means we're on an 'unknown territory' data. + * There's no indication what kind of data we're reading here. + * It could be some text comment, any kind of binary data, + * digital sign, dragons, etc. + * + * We want to find a valid RARv5 magic header inside this unknown + * data. */ + + /* Is it possible in libarchive to just skip everything until the + * end of the file? If so, it would be a better approach than the + * current implementation of this function. */ + + rar5_signature(signature); + + while(1) { + if(!read_ahead(a, chunk_size, &p)) + return ARCHIVE_EOF; + + for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor); + i++) { + if(memcmp(&p[i], signature, + sizeof(rar5_signature_xor)) == 0) { + /* Consume the number of bytes we've used to + * search for the signature, as well as the + * number of bytes used by the signature + * itself. After this we should be standing + * on a valid base block header. */ + (void) consume(a, + i + sizeof(rar5_signature_xor)); + return ARCHIVE_OK; + } + } + + consume(a, chunk_size); + } + + return ARCHIVE_FATAL; +} + +/* This function will switch the multivolume archive file to another file, + * i.e. from part03 to part 04. */ +static int advance_multivolume(struct archive_read* a) { + int lret; + struct rar5* rar = get_context(a); + + /* A small state machine that will skip unnecessary data, needed to + * switch from one multivolume to another. Such skipping is needed if + * we want to be an stream-oriented (instead of file-oriented) + * unpacker. + * + * The state machine starts with `rar->main.endarc` == 0. It also + * assumes that current stream pointer points to some base block + * header. + * + * The `endarc` field is being set when the base block parsing + * function encounters the 'end of archive' marker. + */ + + while(1) { + if(rar->main.endarc == 1) { + int looping = 1; + + rar->main.endarc = 0; + + while(looping) { + lret = skip_base_block(a); + switch(lret) { + case ARCHIVE_RETRY: + /* Continue looping. */ + break; + case ARCHIVE_OK: + /* Break loop. */ + looping = 0; + break; + default: + /* Forward any errors to the + * caller. */ + return lret; + } + } + + break; + } else { + /* Skip current base block. In order to properly skip + * it, we really need to simply parse it and discard + * the results. */ + + lret = skip_base_block(a); + if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED) + return lret; + + /* The `skip_base_block` function tells us if we + * should continue with skipping, or we should stop + * skipping. We're trying to skip everything up to + * a base FILE block. */ + + if(lret != ARCHIVE_RETRY) { + /* If there was an error during skipping, or we + * have just skipped a FILE base block... */ + + if(rar->main.endarc == 0) { + return lret; + } else { + continue; + } + } + } + } + + return ARCHIVE_OK; +} + +/* Merges the partial block from the first multivolume archive file, and + * partial block from the second multivolume archive file. The result is + * a chunk of memory containing the whole block, and the stream pointer + * is advanced to the next block in the second multivolume archive file. */ +static int merge_block(struct archive_read* a, ssize_t block_size, + const uint8_t** p) +{ + struct rar5* rar = get_context(a); + ssize_t cur_block_size, partial_offset = 0; + const uint8_t* lp; + int ret; + + if(rar->merge_mode) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Recursive merge is not allowed"); + + return ARCHIVE_FATAL; + } + + /* Set a flag that we're in the switching mode. */ + rar->cstate.switch_multivolume = 1; + + /* Reallocate the memory which will hold the whole block. */ + if(rar->vol.push_buf) + free((void*) rar->vol.push_buf); + + /* Increasing the allocation block by 8 is due to bit reading functions, + * which are using additional 2 or 4 bytes. Allocating the block size + * by exact value would make bit reader perform reads from invalid + * memory block when reading the last byte from the buffer. */ + rar->vol.push_buf = malloc(block_size + 8); + if(!rar->vol.push_buf) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for a merge block buffer."); + return ARCHIVE_FATAL; + } + + /* Valgrind complains if the extension block for bit reader is not + * initialized, so initialize it. */ + memset(&rar->vol.push_buf[block_size], 0, 8); + + /* A single block can span across multiple multivolume archive files, + * so we use a loop here. This loop will consume enough multivolume + * archive files until the whole block is read. */ + + while(1) { + /* Get the size of current block chunk in this multivolume + * archive file and read it. */ + cur_block_size = rar5_min(rar->file.bytes_remaining, + block_size - partial_offset); + + if(cur_block_size == 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Encountered block size == 0 during block merge"); + return ARCHIVE_FATAL; + } + + if(!read_ahead(a, cur_block_size, &lp)) + return ARCHIVE_EOF; + + /* Sanity check; there should never be a situation where this + * function reads more data than the block's size. */ + if(partial_offset + cur_block_size > block_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Consumed too much data when merging blocks."); + return ARCHIVE_FATAL; + } + + /* Merge previous block chunk with current block chunk, + * or create first block chunk if this is our first + * iteration. */ + memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size); + + /* Advance the stream read pointer by this block chunk size. */ + if(ARCHIVE_OK != consume(a, cur_block_size)) + return ARCHIVE_EOF; + + /* Update the pointers. `partial_offset` contains information + * about the sum of merged block chunks. */ + partial_offset += cur_block_size; + rar->file.bytes_remaining -= cur_block_size; + + /* If `partial_offset` is the same as `block_size`, this means + * we've merged all block chunks and we have a valid full + * block. */ + if(partial_offset == block_size) { + break; + } + + /* If we don't have any bytes to read, this means we should + * switch to another multivolume archive file. */ + if(rar->file.bytes_remaining == 0) { + rar->merge_mode++; + ret = advance_multivolume(a); + rar->merge_mode--; + if(ret != ARCHIVE_OK) { + return ret; + } + } + } + + *p = rar->vol.push_buf; + + /* If we're here, we can resume unpacking by processing the block + * pointed to by the `*p` memory pointer. */ + + return ARCHIVE_OK; +} + +static int process_block(struct archive_read* a) { + const uint8_t* p; + struct rar5* rar = get_context(a); + int ret; + + /* If we don't have any data to be processed, this most probably means + * we need to switch to the next volume. */ + if(rar->main.volume && rar->file.bytes_remaining == 0) { + ret = advance_multivolume(a); + if(ret != ARCHIVE_OK) + return ret; + } + + if(rar->cstate.block_parsing_finished) { + ssize_t block_size; + ssize_t to_skip; + ssize_t cur_block_size; + + /* The header size won't be bigger than 6 bytes. */ + if(!read_ahead(a, 6, &p)) { + /* Failed to prefetch data block header. */ + return ARCHIVE_EOF; + } + + /* + * Read block_size by parsing block header. Validate the header + * by calculating CRC byte stored inside the header. Size of + * the header is not constant (block size can be stored either + * in 1 or 2 bytes), that's why block size is left out from the + * `compressed_block_header` structure and returned by + * `parse_block_header` as the second argument. */ + + ret = parse_block_header(a, p, &block_size, + &rar->last_block_hdr); + if(ret != ARCHIVE_OK) { + return ret; + } + + /* Skip block header. Next data is huffman tables, + * if present. */ + to_skip = sizeof(struct compressed_block_header) + + bf_byte_count(&rar->last_block_hdr) + 1; + + if(ARCHIVE_OK != consume(a, to_skip)) + return ARCHIVE_EOF; + + rar->file.bytes_remaining -= to_skip; + + /* The block size gives information about the whole block size, + * but the block could be stored in split form when using + * multi-volume archives. In this case, the block size will be + * bigger than the actual data stored in this file. Remaining + * part of the data will be in another file. */ + + cur_block_size = + rar5_min(rar->file.bytes_remaining, block_size); + + if(block_size > rar->file.bytes_remaining) { + /* If current blocks' size is bigger than our data + * size, this means we have a multivolume archive. + * In this case, skip all base headers until the end + * of the file, proceed to next "partXXX.rar" volume, + * find its signature, skip all headers up to the first + * FILE base header, and continue from there. + * + * Note that `merge_block` will update the `rar` + * context structure quite extensively. */ + + ret = merge_block(a, block_size, &p); + if(ret != ARCHIVE_OK) { + return ret; + } + + cur_block_size = block_size; + + /* Current stream pointer should be now directly + * *after* the block that spanned through multiple + * archive files. `p` pointer should have the data of + * the *whole* block (merged from partial blocks + * stored in multiple archives files). */ + } else { + rar->cstate.switch_multivolume = 0; + + /* Read the whole block size into memory. This can take + * up to 8 megabytes of memory in theoretical cases. + * Might be worth to optimize this and use a standard + * chunk of 4kb's. */ + if(!read_ahead(a, 4 + cur_block_size, &p)) { + /* Failed to prefetch block data. */ + return ARCHIVE_EOF; + } + } + + rar->cstate.block_buf = p; + rar->cstate.cur_block_size = cur_block_size; + rar->cstate.block_parsing_finished = 0; + + rar->bits.in_addr = 0; + rar->bits.bit_addr = 0; + + if(bf_is_table_present(&rar->last_block_hdr)) { + /* Load Huffman tables. */ + ret = parse_tables(a, rar, p); + if(ret != ARCHIVE_OK) { + /* Error during decompression of Huffman + * tables. */ + return ret; + } + } + } else { + /* Block parsing not finished, reuse previous memory buffer. */ + p = rar->cstate.block_buf; + } + + /* Uncompress the block, or a part of it, depending on how many bytes + * will be generated by uncompressing the block. + * + * In case too many bytes will be generated, calling this function + * again will resume the uncompression operation. */ + ret = do_uncompress_block(a, p); + if(ret != ARCHIVE_OK) { + return ret; + } + + if(rar->cstate.block_parsing_finished && + rar->cstate.switch_multivolume == 0 && + rar->cstate.cur_block_size > 0) + { + /* If we're processing a normal block, consume the whole + * block. We can do this because we've already read the whole + * block to memory. */ + if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size)) + return ARCHIVE_FATAL; + + rar->file.bytes_remaining -= rar->cstate.cur_block_size; + } else if(rar->cstate.switch_multivolume) { + /* Don't consume the block if we're doing multivolume + * processing. The volume switching function will consume + * the proper count of bytes instead. */ + rar->cstate.switch_multivolume = 0; + } + + return ARCHIVE_OK; +} + +/* Pops the `buf`, `size` and `offset` from the "data ready" stack. + * + * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY + * when there is no data on the stack. */ +static int use_data(struct rar5* rar, const void** buf, size_t* size, + int64_t* offset) +{ + int i; + + for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { + struct data_ready *d = &rar->cstate.dready[i]; + + if(d->used) { + if(buf) *buf = d->buf; + if(size) *size = d->size; + if(offset) *offset = d->offset; + + d->used = 0; + return ARCHIVE_OK; + } + } + + return ARCHIVE_RETRY; +} + +/* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready + * FIFO stack. Those values will be popped from this stack by the `use_data` + * function. */ +static int push_data_ready(struct archive_read* a, struct rar5* rar, + const uint8_t* buf, size_t size, int64_t offset) +{ + int i; + + /* Don't push if we're in skip mode. This is needed because solid + * streams need full processing even if we're skipping data. After + * fully processing the stream, we need to discard the generated bytes, + * because we're interested only in the side effect: building up the + * internal window circular buffer. This window buffer will be used + * later during unpacking of requested data. */ + if(rar->skip_mode) + return ARCHIVE_OK; + + /* Sanity check. */ + if(offset != rar->file.last_offset + rar->file.last_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Sanity check error: output stream is not continuous"); + return ARCHIVE_FATAL; + } + + for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { + struct data_ready* d = &rar->cstate.dready[i]; + if(!d->used) { + d->used = 1; + d->buf = buf; + d->size = size; + d->offset = offset; + + /* These fields are used only in sanity checking. */ + rar->file.last_offset = offset; + rar->file.last_size = size; + + /* Calculate the checksum of this new block before + * submitting data to libarchive's engine. */ + update_crc(rar, d->buf, d->size); + + return ARCHIVE_OK; + } + } + + /* Program counter will reach this code if the `rar->cstate.data_ready` + * stack will be filled up so that no new entries will be allowed. The + * code shouldn't allow such situation to occur. So we treat this case + * as an internal error. */ + + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Error: premature end of data_ready stack"); + return ARCHIVE_FATAL; +} + +/* This function uncompresses the data that is stored in the base + * block. + * + * The FILE base block looks like this: + * + *
... + * + * The
is a block header, that is parsed in parse_block_header(). + * It's a "compressed_block_header" structure, containing metadata needed + * to know when we should stop looking for more blocks. + * + * contain data needed to set up the huffman tables, needed + * for the actual decompression. + * + * Each consists of series of literals: + * + * ... + * + * Those literals generate the uncompression data. They operate on a circular + * buffer, sometimes writing raw data into it, sometimes referencing + * some previous data inside this buffer, and sometimes declaring a filter + * that will need to be executed on the data stored in the circular buffer. + * It all depends on the literal that is used. + * + * Sometimes blocks produce output data, sometimes they don't. For example, for + * some huge files that use lots of filters, sometimes a block is filled with + * only filter declaration literals. Such blocks won't produce any data in the + * circular buffer. + * + * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte, + * because a literal can reference previously decompressed data. For example, + * there can be a literal that says: 'append a byte 0xFE here', and after + * it another literal can say 'append 1 megabyte of data from circular buffer + * offset 0x12345'. This is how RAR format handles compressing repeated + * patterns. + * + * The RAR compressor creates those literals and the actual efficiency of + * compression depends on what those literals are. The literals can also + * be seen as a kind of a non-turing-complete virtual machine that simply + * tells the decompressor what it should do. + * */ + +static int do_uncompress_file(struct archive_read* a) { + struct rar5* rar = get_context(a); + int ret; + int64_t max_end_pos; + + if(!rar->cstate.initialized) { + /* Don't perform full context reinitialization if we're + * processing a solid archive. */ + if(!rar->main.solid || !rar->cstate.window_buf) { + init_unpack(rar); + } + + rar->cstate.initialized = 1; + } + + if(rar->cstate.all_filters_applied == 1) { + /* We use while(1) here, but standard case allows for just 1 + * iteration. The loop will iterate if process_block() didn't + * generate any data at all. This can happen if the block + * contains only filter definitions (this is common in big + * files). */ + while(1) { + ret = process_block(a); + if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL) + return ret; + + if(rar->cstate.last_write_ptr == + rar->cstate.write_ptr) { + /* The block didn't generate any new data, + * so just process a new block. */ + continue; + } + + /* The block has generated some new data, so break + * the loop. */ + break; + } + } + + /* Try to run filters. If filters won't be applied, it means that + * insufficient data was generated. */ + ret = apply_filters(a); + if(ret == ARCHIVE_RETRY) { + return ARCHIVE_OK; + } else if(ret == ARCHIVE_FATAL) { + return ARCHIVE_FATAL; + } + + /* If apply_filters() will return ARCHIVE_OK, we can continue here. */ + + if(cdeque_size(&rar->cstate.filters) > 0) { + /* Check if we can write something before hitting first + * filter. */ + struct filter_info* flt; + + /* Get the block_start offset from the first filter. */ + if(CDE_OK != cdeque_front(&rar->cstate.filters, + cdeque_filter_p(&flt))) + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Can't read first filter"); + return ARCHIVE_FATAL; + } + + max_end_pos = rar5_min(flt->block_start, + rar->cstate.write_ptr); + } else { + /* There are no filters defined, or all filters were applied. + * This means we can just store the data without any + * postprocessing. */ + max_end_pos = rar->cstate.write_ptr; + } + + if(max_end_pos == rar->cstate.last_write_ptr) { + /* We can't write anything yet. The block uncompression + * function did not generate enough data, and no filter can be + * applied. At the same time we don't have any data that can be + * stored without filter postprocessing. This means we need to + * wait for more data to be generated, so we can apply the + * filters. + * + * Signal the caller that we need more data to be able to do + * anything. + */ + return ARCHIVE_RETRY; + } else { + /* We can write the data before hitting the first filter. + * So let's do it. The push_window_data() function will + * effectively return the selected data block to the user + * application. */ + push_window_data(a, rar, rar->cstate.last_write_ptr, + max_end_pos); + rar->cstate.last_write_ptr = max_end_pos; + } + + return ARCHIVE_OK; +} + +static int uncompress_file(struct archive_read* a) { + int ret; + + while(1) { + /* Sometimes the uncompression function will return a + * 'retry' signal. If this will happen, we have to retry + * the function. */ + ret = do_uncompress_file(a); + if(ret != ARCHIVE_RETRY) + return ret; + } +} + + +static int do_unstore_file(struct archive_read* a, + struct rar5* rar, const void** buf, size_t* size, int64_t* offset) +{ + size_t to_read; + const uint8_t* p; + + if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 && + rar->generic.split_after > 0) + { + int ret; + + rar->cstate.switch_multivolume = 1; + ret = advance_multivolume(a); + rar->cstate.switch_multivolume = 0; + + if(ret != ARCHIVE_OK) { + /* Failed to advance to next multivolume archive + * file. */ + return ret; + } + } + + to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024); + if(to_read == 0) { + return ARCHIVE_EOF; + } + + if(!read_ahead(a, to_read, &p)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "I/O error when unstoring file"); + return ARCHIVE_FATAL; + } + + if(ARCHIVE_OK != consume(a, to_read)) { + return ARCHIVE_EOF; + } + + if(buf) *buf = p; + if(size) *size = to_read; + if(offset) *offset = rar->cstate.last_unstore_ptr; + + rar->file.bytes_remaining -= to_read; + rar->cstate.last_unstore_ptr += to_read; + + update_crc(rar, p, to_read); + return ARCHIVE_OK; +} + +static int do_unpack(struct archive_read* a, struct rar5* rar, + const void** buf, size_t* size, int64_t* offset) +{ + enum COMPRESSION_METHOD { + STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4, + BEST = 5 + }; + + if(rar->file.service > 0) { + return do_unstore_file(a, rar, buf, size, offset); + } else { + switch(rar->cstate.method) { + case STORE: + return do_unstore_file(a, rar, buf, size, + offset); + case FASTEST: + /* fallthrough */ + case FAST: + /* fallthrough */ + case NORMAL: + /* fallthrough */ + case GOOD: + /* fallthrough */ + case BEST: + return uncompress_file(a); + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Compression method not supported: 0x%x", + rar->cstate.method); + + return ARCHIVE_FATAL; + } + } + +#if !defined WIN32 + /* Not reached. */ + return ARCHIVE_OK; +#endif +} + +static int verify_checksums(struct archive_read* a) { + int verify_crc; + struct rar5* rar = get_context(a); + + /* Check checksums only when actually unpacking the data. There's no + * need to calculate checksum when we're skipping data in solid archives + * (skipping in solid archives is the same thing as unpacking compressed + * data and discarding the result). */ + + if(!rar->skip_mode) { + /* Always check checksums if we're not in skip mode */ + verify_crc = 1; + } else { + /* We can override the logic above with a compile-time option + * NO_CRC_ON_SOLID_SKIP. This option is used during debugging, + * and it will check checksums of unpacked data even when + * we're skipping it. */ + +#if defined CHECK_CRC_ON_SOLID_SKIP + /* Debug case */ + verify_crc = 1; +#else + /* Normal case */ + verify_crc = 0; +#endif + } + + if(verify_crc) { + /* During unpacking, on each unpacked block we're calling the + * update_crc() function. Since we are here, the unpacking + * process is already over and we can check if calculated + * checksum (CRC32 or BLAKE2sp) is the same as what is stored + * in the archive. */ + if(rar->file.stored_crc32 > 0) { + /* Check CRC32 only when the file contains a CRC32 + * value for this file. */ + + if(rar->file.calculated_crc32 != + rar->file.stored_crc32) { + /* Checksums do not match; the unpacked file + * is corrupted. */ + + DEBUG_CODE { + printf("Checksum error: CRC32 " + "(was: %08x, expected: %08x)\n", + rar->file.calculated_crc32, + rar->file.stored_crc32); + } + +#ifndef DONT_FAIL_ON_CRC_ERROR + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Checksum error: CRC32"); + return ARCHIVE_FATAL; +#endif + } else { + DEBUG_CODE { + printf("Checksum OK: CRC32 " + "(%08x/%08x)\n", + rar->file.stored_crc32, + rar->file.calculated_crc32); + } + } + } + + if(rar->file.has_blake2 > 0) { + /* BLAKE2sp is an optional checksum algorithm that is + * added to RARv5 archives when using the `-htb` switch + * during creation of archive. + * + * We now finalize the hash calculation by calling the + * `final` function. This will generate the final hash + * value we can use to compare it with the BLAKE2sp + * checksum that is stored in the archive. + * + * The return value of this `final` function is not + * very helpful, as it guards only against improper use. + * This is why we're explicitly ignoring it. */ + + uint8_t b2_buf[32]; + (void) blake2sp_final(&rar->file.b2state, b2_buf, 32); + + if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) { +#ifndef DONT_FAIL_ON_CRC_ERROR + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Checksum error: BLAKE2"); + + return ARCHIVE_FATAL; +#endif + } + } + } + + /* Finalization for this file has been successfully completed. */ + return ARCHIVE_OK; +} + +static int verify_global_checksums(struct archive_read* a) { + return verify_checksums(a); +} + +/* + * Decryption function for the magic signature pattern. Check the comment near + * the `rar5_signature_xor` symbol to read the rationale behind this. + */ +static void rar5_signature(char *buf) { + size_t i; + + for(i = 0; i < sizeof(rar5_signature_xor); i++) { + buf[i] = rar5_signature_xor[i] ^ 0xA1; + } +} + +static int rar5_read_data(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) { + int ret; + struct rar5* rar = get_context(a); + + if(rar->file.dir > 0) { + /* Don't process any data if this file entry was declared + * as a directory. This is needed, because entries marked as + * directory doesn't have any dictionary buffer allocated, so + * it's impossible to perform any decompression. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Can't decompress an entry marked as a directory"); + return ARCHIVE_FAILED; + } + + if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Unpacker has written too many bytes"); + return ARCHIVE_FATAL; + } + + ret = use_data(rar, buff, size, offset); + if(ret == ARCHIVE_OK) { + return ret; + } + + if(rar->file.eof == 1) { + return ARCHIVE_EOF; + } + + ret = do_unpack(a, rar, buff, size, offset); + if(ret != ARCHIVE_OK) { + return ret; + } + + if(rar->file.bytes_remaining == 0 && + rar->cstate.last_write_ptr == rar->file.unpacked_size) + { + /* If all bytes of current file were processed, run + * finalization. + * + * Finalization will check checksum against proper values. If + * some of the checksums will not match, we'll return an error + * value in the last `archive_read_data` call to signal an error + * to the user. */ + + rar->file.eof = 1; + return verify_global_checksums(a); + } + + return ARCHIVE_OK; +} + +static int rar5_read_data_skip(struct archive_read *a) { + struct rar5* rar = get_context(a); + + if(rar->main.solid) { + /* In solid archives, instead of skipping the data, we need to + * extract it, and dispose the result. The side effect of this + * operation will be setting up the initial window buffer state + * needed to be able to extract the selected file. */ + + int ret; + + /* Make sure to process all blocks in the compressed stream. */ + while(rar->file.bytes_remaining > 0) { + /* Setting the "skip mode" will allow us to skip + * checksum checks during data skipping. Checking the + * checksum of skipped data isn't really necessary and + * it's only slowing things down. + * + * This is incremented instead of setting to 1 because + * this data skipping function can be called + * recursively. */ + rar->skip_mode++; + + /* We're disposing 1 block of data, so we use triple + * NULLs in arguments. */ + ret = rar5_read_data(a, NULL, NULL, NULL); + + /* Turn off "skip mode". */ + rar->skip_mode--; + + if(ret < 0 || ret == ARCHIVE_EOF) { + /* Propagate any potential error conditions + * to the caller. */ + return ret; + } + } + } else { + /* In standard archives, we can just jump over the compressed + * stream. Each file in non-solid archives starts from an empty + * window buffer. */ + + if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) { + return ARCHIVE_FATAL; + } + + rar->file.bytes_remaining = 0; + } + + return ARCHIVE_OK; +} + +static int64_t rar5_seek_data(struct archive_read *a, int64_t offset, + int whence) +{ + (void) a; + (void) offset; + (void) whence; + + /* We're a streaming unpacker, and we don't support seeking. */ + + return ARCHIVE_FATAL; +} + +static int rar5_cleanup(struct archive_read *a) { + struct rar5* rar = get_context(a); + + free(rar->cstate.window_buf); + free(rar->cstate.filtered_buf); + + free(rar->vol.push_buf); + + free_filters(rar); + cdeque_free(&rar->cstate.filters); + + free(rar); + a->format->data = NULL; + + return ARCHIVE_OK; +} + +static int rar5_capabilities(struct archive_read * a) { + (void) a; + return 0; +} + +static int rar5_has_encrypted_entries(struct archive_read *_a) { + (void) _a; + + /* Unsupported for now. */ + return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED; +} + +static int rar5_init(struct rar5* rar) { + memset(rar, 0, sizeof(struct rar5)); + + if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192)) + return ARCHIVE_FATAL; + + return ARCHIVE_OK; +} + +int archive_read_support_format_rar5(struct archive *_a) { + struct archive_read* ar; + int ret; + struct rar5* rar; + + if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar))) + return ret; + + rar = malloc(sizeof(*rar)); + if(rar == NULL) { + archive_set_error(&ar->archive, ENOMEM, + "Can't allocate rar5 data"); + return ARCHIVE_FATAL; + } + + if(ARCHIVE_OK != rar5_init(rar)) { + archive_set_error(&ar->archive, ENOMEM, + "Can't allocate rar5 filter buffer"); + return ARCHIVE_FATAL; + } + + ret = __archive_read_register_format(ar, + rar, + "rar5", + rar5_bid, + rar5_options, + rar5_read_header, + rar5_read_data, + rar5_read_data_skip, + rar5_seek_data, + rar5_cleanup, + rar5_capabilities, + rar5_has_encrypted_entries); + + if(ret != ARCHIVE_OK) { + (void) rar5_cleanup(ar); + } + + return ret; +} diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_raw.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_raw.c index efa2c6a33c7..ec0520b60a6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_raw.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_raw.c @@ -120,7 +120,9 @@ archive_read_format_raw_read_header(struct archive_read *a, archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_perm(entry, 0644); /* I'm deliberately leaving most fields unset here. */ - return (ARCHIVE_OK); + + /* Let the filter fill out any fields it might have. */ + return __archive_read_header(a, entry); } static int diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c index 60800bb812e..c63d46fc0ce 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c @@ -694,11 +694,13 @@ tar_read_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, size_t *unconsumed) { ssize_t bytes; - int err; + int err, eof_vol_header; const char *h; const struct archive_entry_header_ustar *header; const struct archive_entry_header_gnutar *gnuheader; + eof_vol_header = 0; + /* Loop until we find a workable header record. */ for (;;) { tar_flush_unconsumed(a, unconsumed); @@ -788,6 +790,8 @@ tar_read_header(struct archive_read *a, struct tar *tar, break; case 'V': /* GNU volume header */ err = header_volume(a, tar, entry, h, unconsumed); + if (err == ARCHIVE_EOF) + eof_vol_header = 1; break; case 'X': /* Used by SUN tar; same as 'x'. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; @@ -862,9 +866,17 @@ tar_read_header(struct archive_read *a, struct tar *tar, } return (err); } - if (err == ARCHIVE_EOF) - /* EOF when recursively reading a header is bad. */ - archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); + if (err == ARCHIVE_EOF) { + if (!eof_vol_header) { + /* EOF when recursively reading a header is bad. */ + archive_set_error(&a->archive, EINVAL, + "Damaged tar archive"); + } else { + /* If we encounter just a GNU volume header treat + * this situation as an empty archive */ + return (ARCHIVE_EOF); + } + } return (ARCHIVE_FATAL); } @@ -1942,6 +1954,15 @@ pax_attribute(struct archive_read *a, struct tar *tar, pax_time(value, &s, &n); archive_entry_set_birthtime(entry, s, n); } + if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) { + if (strcmp(value, "file") == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_FILE); + } else if (strcmp(value, "dir") == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_DIRECTORY); + } + } if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0) pax_attribute_xattr(entry, key, value); break; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c index e8753853f3c..72977b8e073 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c @@ -386,6 +386,11 @@ _warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off) return (ARCHIVE_EOF); } + if (w->unconsumed) { + __archive_read_consume(a, w->unconsumed); + w->unconsumed = 0U; + } + rab = __archive_read_ahead(a, 1U, &nrd); if (nrd < 0) { *bsz = 0U; @@ -621,7 +626,8 @@ _warc_rdver(const char *buf, size_t bsz) if (ver >= 1200U) { if (memcmp(c, "\r\n", 2U) != 0) ver = 0U; - } else if (ver < 1200U) { + } else { + /* ver < 1200U */ if (*c != ' ' && *c != '\t') ver = 0U; } @@ -739,8 +745,9 @@ _warc_rdlen(const char *buf, size_t bsz) /* there must be at least one digit */ if (!isdigit((unsigned char)*val)) return -1; + errno = 0; len = strtol(val, &on, 10); - if (on != eol) { + if (errno != 0 || on != eol) { /* line must end here */ return -1; } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c index 9292ed784d0..9489e510603 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c @@ -39,13 +39,13 @@ __FBSDID("$FreeBSD$"); #include #endif #ifdef HAVE_BZLIB_H -#include +#include #endif #if HAVE_LZMA_H -#include +#include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" @@ -167,6 +167,9 @@ struct xar_file { #define HAS_FFLAGS 0x01000 #define HAS_XATTR 0x02000 #define HAS_ACL 0x04000 +#define HAS_CTIME 0x08000 +#define HAS_MTIME 0x10000 +#define HAS_ATIME 0x20000 uint64_t id; uint64_t length; @@ -695,9 +698,15 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) */ file_free(file); } - archive_entry_set_atime(entry, file->atime, 0); - archive_entry_set_ctime(entry, file->ctime, 0); - archive_entry_set_mtime(entry, file->mtime, 0); + if (file->has & HAS_ATIME) { + archive_entry_set_atime(entry, file->atime, 0); + } + if (file->has & HAS_CTIME) { + archive_entry_set_ctime(entry, file->ctime, 0); + } + if (file->has & HAS_MTIME) { + archive_entry_set_mtime(entry, file->mtime, 0); + } archive_entry_set_gid(entry, file->gid); if (file->gname.length > 0 && archive_entry_copy_gname_l(entry, file->gname.s, @@ -789,7 +798,8 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) xattr = file->xattr_list; while (xattr != NULL) { const void *d; - size_t outbytes, used; + size_t outbytes = 0; + size_t used = 0; r = move_reading_point(a, xattr->offset); if (r != ARCHIVE_OK) @@ -811,8 +821,18 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) r = checksum_final(a, xattr->a_sum.val, xattr->a_sum.len, xattr->e_sum.val, xattr->e_sum.len); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "Xattr checksum error"); + r = ARCHIVE_WARN; break; + } + if (xattr->name.s == NULL) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "Xattr name error"); + r = ARCHIVE_WARN; + break; + } archive_entry_xattr_add_entry(entry, xattr->name.s, d, outbytes); xattr = xattr->next; @@ -838,7 +858,7 @@ xar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct xar *xar; - size_t used; + size_t used = 0; int r; xar = (struct xar *)(a->format->data); @@ -967,7 +987,7 @@ move_reading_point(struct archive_read *a, uint64_t offset) return ((int)step); xar->offset += step; } else { - int64_t pos = __archive_read_seek(a, offset, SEEK_SET); + int64_t pos = __archive_read_seek(a, xar->h_base + offset, SEEK_SET); if (pos == ARCHIVE_FAILED) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, @@ -1220,8 +1240,7 @@ heap_add_entry(struct archive_read *a, } memcpy(new_pending_files, heap->files, heap->allocated * sizeof(new_pending_files[0])); - if (heap->files != NULL) - free(heap->files); + free(heap->files); heap->files = new_pending_files; heap->allocated = new_size; } @@ -1767,8 +1786,8 @@ file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list) } file->parent = xar->file; file->mode = 0777 | AE_IFREG; - file->atime = time(NULL); - file->mtime = time(NULL); + file->atime = 0; + file->mtime = 0; xar->file = file; xar->xattr = NULL; for (attr = list->first; attr != NULL; attr = attr->next) { @@ -2594,15 +2613,14 @@ strappend_base64(struct xar *xar, while (l > 0) { int n = 0; - if (l > 0) { - if (base64[b[0]] < 0 || base64[b[1]] < 0) - break; - n = base64[*b++] << 18; - n |= base64[*b++] << 12; - *out++ = n >> 16; - len++; - l -= 2; - } + if (base64[b[0]] < 0 || base64[b[1]] < 0) + break; + n = base64[*b++] << 18; + n |= base64[*b++] << 12; + *out++ = n >> 16; + len++; + l -= 2; + if (l > 0) { if (base64[*b] < 0) break; @@ -2751,15 +2769,15 @@ xml_data(void *userData, const char *s, int len) xar->file->uid = atol10(s, len); break; case FILE_CTIME: - xar->file->has |= HAS_TIME; + xar->file->has |= HAS_TIME | HAS_CTIME; xar->file->ctime = parse_time(s, len); break; case FILE_MTIME: - xar->file->has |= HAS_TIME; + xar->file->has |= HAS_TIME | HAS_MTIME; xar->file->mtime = parse_time(s, len); break; case FILE_ATIME: - xar->file->has |= HAS_TIME; + xar->file->has |= HAS_TIME | HAS_ATIME; xar->file->atime = parse_time(s, len); break; case FILE_DATA_LENGTH: diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c index 7e99b1275e9..4d71f985a2d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c @@ -50,7 +50,13 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 #include #endif #ifdef HAVE_ZLIB_H -#include +#include +#endif +#ifdef HAVE_BZLIB_H +#include +#endif +#ifdef HAVE_LZMA_H +#include #endif #include "archive.h" @@ -63,6 +69,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 #include "archive_private.h" #include "archive_rb.h" #include "archive_read_private.h" +#include "archive_ppmd8_private.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" @@ -165,13 +172,30 @@ struct zip { char decompress_init; char end_of_entry; -#ifdef HAVE_ZLIB_H unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; + +#ifdef HAVE_ZLIB_H z_stream stream; char stream_valid; #endif +#if HAVE_LZMA_H && HAVE_LIBLZMA + lzma_stream zipx_lzma_stream; + char zipx_lzma_valid; +#endif + +#ifdef HAVE_BZLIB_H + bz_stream bzstream; + char bzstream_valid; +#endif + + IByteIn zipx_ppmd_stream; + ssize_t zipx_ppmd_read_compressed; + CPpmd8 ppmd8; + char ppmd8_valid; + char ppmd8_stream_failed; + struct archive_string_conv *sconv; struct archive_string_conv *sconv_default; struct archive_string_conv *sconv_utf8; @@ -222,6 +246,33 @@ struct zip { /* Many systems define min or MIN, but not all. */ #define zipmin(a,b) ((a) < (b) ? (a) : (b)) +/* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8 + * streams inside ZIP files. It has 2 purposes: one is to fetch the next + * compressed byte from the stream, second one is to increase the counter how + * many compressed bytes were read. */ +static Byte +ppmd_read(void* p) { + /* Get the handle to current decompression context. */ + struct archive_read *a = ((IByteIn*)p)->a; + struct zip *zip = (struct zip*) a->format->data; + ssize_t bytes_avail = 0; + + /* Fetch next byte. */ + const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail); + if(bytes_avail < 1) { + zip->ppmd8_stream_failed = 1; + return 0; + } + + __archive_read_consume(a, 1); + + /* Increment the counter. */ + ++zip->zipx_ppmd_read_compressed; + + /* Return the next compressed byte. */ + return data[0]; +} + /* ------------------------------------------------------------------------ */ /* @@ -372,6 +423,8 @@ static const struct { {17, "reserved"}, /* Reserved by PKWARE */ {18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */ {19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */ + {95, "xz"}, /* XZ compressed data */ + {96, "jpeg"}, /* JPEG compressed data */ {97, "wav-pack"}, /* WavPack compressed data */ {98, "ppmd-1"}, /* PPMd version I, Rev 1 */ {99, "aes"} /* WinZip AES encryption */ @@ -419,27 +472,49 @@ zip_time(const char *p) * triplets. id and size are 2 bytes each. */ static int -process_extra(struct archive_read *a, const char *p, size_t extra_length, struct zip_entry* zip_entry) +process_extra(struct archive_read *a, struct archive_entry *entry, + const char *p, size_t extra_length, struct zip_entry* zip_entry) { unsigned offset = 0; + struct zip *zip = (struct zip *)(a->format->data); if (extra_length == 0) { return ARCHIVE_OK; } if (extra_length < 4) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Too-small extra data: Need at least 4 bytes, but only found %d bytes", (int)extra_length); - return ARCHIVE_FAILED; + size_t i = 0; + /* Some ZIP files may have trailing 0 bytes. Let's check they + * are all 0 and ignore them instead of returning an error. + * + * This is not technically correct, but some ZIP files look + * like this and other tools support those files - so let's + * also support them. + */ + for (; i < extra_length; i++) { + if (p[i] != 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Too-small extra data: " + "Need at least 4 bytes, " + "but only found %d bytes", + (int)extra_length); + return ARCHIVE_FAILED; + } + } + + return ARCHIVE_OK; } + while (offset <= extra_length - 4) { unsigned short headerid = archive_le16dec(p + offset); unsigned short datasize = archive_le16dec(p + offset + 2); offset += 4; if (offset + datasize > extra_length) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Extra data overflow: Need %d bytes but only found %d bytes", + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: " + "Need %d bytes but only found %d bytes", (int)datasize, (int)(extra_length - offset)); return ARCHIVE_FAILED; } @@ -454,9 +529,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct if (zip_entry->uncompressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 - || (t = archive_le64dec(p + offset)) > INT64_MAX) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed 64-bit uncompressed size"); + || (t = archive_le64dec(p + offset)) > + INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed 64-bit " + "uncompressed size"); return ARCHIVE_FAILED; } zip_entry->uncompressed_size = t; @@ -466,9 +544,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct if (zip_entry->compressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 - || (t = archive_le64dec(p + offset)) > INT64_MAX) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed 64-bit compressed size"); + || (t = archive_le64dec(p + offset)) > + INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed 64-bit " + "compressed size"); return ARCHIVE_FAILED; } zip_entry->compressed_size = t; @@ -478,9 +559,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct if (zip_entry->local_header_offset == 0xffffffff) { uint64_t t = 0; if (datasize < 8 - || (t = archive_le64dec(p + offset)) > INT64_MAX) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed 64-bit local header offset"); + || (t = archive_le64dec(p + offset)) > + INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed 64-bit " + "local header offset"); return ARCHIVE_FAILED; } zip_entry->local_header_offset = t; @@ -513,7 +597,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct /* Extended time field "UT". */ int flags; if (datasize == 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Incomplete extended time field"); return ARCHIVE_FAILED; } @@ -595,7 +680,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct * if bitmap & 1, 2 byte "version made by" * if bitmap & 2, 2 byte "internal file attributes" * if bitmap & 4, 4 byte "external file attributes" - * if bitmap & 8, 2 byte comment length + n byte comment + * if bitmap & 8, 2 byte comment length + n byte + * comment */ int bitmap, bitmap_last; @@ -646,13 +732,18 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct = external_attributes >> 16; } else if (zip_entry->system == 0) { // Interpret MSDOS directory bit - if (0x10 == (external_attributes & 0x10)) { - zip_entry->mode = AE_IFDIR | 0775; + if (0x10 == (external_attributes & + 0x10)) { + zip_entry->mode = + AE_IFDIR | 0775; } else { - zip_entry->mode = AE_IFREG | 0664; + zip_entry->mode = + AE_IFREG | 0664; } - if (0x01 == (external_attributes & 0x01)) { - // Read-only bit; strip write permissions + if (0x01 == (external_attributes & + 0x01)) { + /* Read-only bit; + * strip write permissions */ zip_entry->mode &= 0555; } } else { @@ -679,6 +770,59 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct } break; } + case 0x7075: + { + /* Info-ZIP Unicode Path Extra Field. */ + if (datasize < 5 || entry == NULL) + break; + offset += 5; + datasize -= 5; + + /* The path name in this field is always encoded + * in UTF-8. */ + if (zip->sconv_utf8 == NULL) { + zip->sconv_utf8 = + archive_string_conversion_from_charset( + &a->archive, "UTF-8", 1); + /* If the converter from UTF-8 is not + * available, then the path name from the main + * field will more likely be correct. */ + if (zip->sconv_utf8 == NULL) + break; + } + + /* Make sure the CRC32 of the filename matches. */ + if (!zip->ignore_crc32) { + const char *cp = archive_entry_pathname(entry); + if (cp) { + unsigned long file_crc = + zip->crc32func(0, cp, strlen(cp)); + unsigned long utf_crc = + archive_le32dec(p + offset - 4); + if (file_crc != utf_crc) { +#ifdef DEBUG + fprintf(stderr, + "CRC filename mismatch; " + "CDE is %lx, but UTF8 " + "is outdated with %lx\n", + file_crc, utf_crc); +#endif + break; + } + } + } + + if (archive_entry_copy_pathname_l(entry, + p + offset, datasize, zip->sconv_utf8) != 0) { + /* Ignore the error, and fallback to the path + * name from the main field. */ +#ifdef DEBUG + fprintf(stderr, "Failed to read the ZIP " + "0x7075 extra field path.\n"); +#endif + } + break; + } case 0x7855: /* Info-ZIP Unix Extra Field (type 2) "Ux". */ #ifdef DEBUG @@ -713,7 +857,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct } if (datasize >= (2 + uidsize + 3)) { /* get a gid size. */ - gidsize = 0xff & (int)p[offset+2+uidsize]; + gidsize = 0xff & + (int)p[offset+2+uidsize]; if (gidsize == 2) zip_entry->gid = archive_le16dec( @@ -730,7 +875,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct case 0x9901: /* WinZip AES extra data field. */ if (datasize < 6) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Incomplete AES field"); return ARCHIVE_FAILED; } @@ -750,12 +896,6 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct } offset += datasize; } - if (offset != extra_length) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed extra data: Consumed %d bytes of %d bytes", - (int)offset, (int)extra_length); - return ARCHIVE_FAILED; - } return ARCHIVE_OK; } @@ -875,7 +1015,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } - if (ARCHIVE_OK != process_extra(a, h, extra_length, zip_entry)) { + if (ARCHIVE_OK != process_extra(a, entry, h, extra_length, + zip_entry)) { return ARCHIVE_FATAL; } __archive_read_consume(a, extra_length); @@ -892,8 +1033,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, zip_entry->mode |= 0664; } - /* Windows archivers sometimes use backslash as the directory separator. - Normalize to slash. */ + /* Windows archivers sometimes use backslash as the directory + * separator. Normalize to slash. */ if (zip_entry->system == 0 && (wp = archive_entry_pathname_w(entry)) != NULL) { if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) { @@ -912,7 +1053,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, /* Make sure that entries with a trailing '/' are marked as directories * even if the External File Attributes contains bogus values. If this - * is not a directory and there is no type, assume regularfile. */ + * is not a directory and there is no type, assume a regular file. */ if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) { int has_slash; @@ -963,7 +1104,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, } if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) { - /* If this came from the central dir, it's size info + /* If this came from the central dir, its size info * is definitive, so ignore the length-at-end flag. */ zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END; /* If local header is missing a value, use the one from @@ -1202,7 +1343,8 @@ zip_read_data_none(struct archive_read *a, const void **_buff, zip->entry->crc32 = archive_le32dec(p + 4); compressed = archive_le64dec(p + 8); uncompressed = archive_le64dec(p + 16); - if (compressed > INT64_MAX || uncompressed > INT64_MAX) { + if (compressed > INT64_MAX || uncompressed > + INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Overflow of 64-bit file sizes"); @@ -1296,6 +1438,745 @@ zip_read_data_none(struct archive_read *a, const void **_buff, return (ARCHIVE_OK); } +static int +consume_optional_marker(struct archive_read *a, struct zip *zip) +{ + if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { + const char *p; + + if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP end-of-file record"); + return (ARCHIVE_FATAL); + } + /* Consume the optional PK\007\010 marker. */ + if (p[0] == 'P' && p[1] == 'K' && + p[2] == '\007' && p[3] == '\010') { + p += 4; + zip->unconsumed = 4; + } + if (zip->entry->flags & LA_USED_ZIP64) { + uint64_t compressed, uncompressed; + zip->entry->crc32 = archive_le32dec(p); + compressed = archive_le64dec(p + 4); + uncompressed = archive_le64dec(p + 12); + if (compressed > INT64_MAX || + uncompressed > INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Overflow of 64-bit file sizes"); + return ARCHIVE_FAILED; + } + zip->entry->compressed_size = compressed; + zip->entry->uncompressed_size = uncompressed; + zip->unconsumed += 20; + } else { + zip->entry->crc32 = archive_le32dec(p); + zip->entry->compressed_size = archive_le32dec(p + 4); + zip->entry->uncompressed_size = archive_le32dec(p + 8); + zip->unconsumed += 12; + } + } + + return (ARCHIVE_OK); +} + +#if HAVE_LZMA_H && HAVE_LIBLZMA +static int +zipx_xz_init(struct archive_read *a, struct zip *zip) +{ + lzma_ret r; + + if(zip->zipx_lzma_valid) { + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + } + + memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); + r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0); + if (r != LZMA_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "xz initialization failed(%d)", + r); + + return (ARCHIVE_FAILED); + } + + zip->zipx_lzma_valid = 1; + + free(zip->uncompressed_buffer); + + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for xz decompression"); + return (ARCHIVE_FATAL); + } + + zip->decompress_init = 1; + return (ARCHIVE_OK); +} + +static int +zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) +{ + lzma_ret r; + const uint8_t* p; + +#pragma pack(push) +#pragma pack(1) + struct _alone_header { + uint8_t bytes[5]; + uint64_t uncompressed_size; + } alone_header; +#pragma pack(pop) + + if(zip->zipx_lzma_valid) { + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + } + + /* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma + * that is a part of XZ Utils. The stream format stored inside ZIPX + * file is a modified "lzma alone" file format, that was used by the + * `lzma` utility which was later deprecated in favour of `xz` utility. * Since those formats are nearly the same, we can use a standard + * "lzma alone" decoder from XZ Utils. */ + + memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); + r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX); + if (r != LZMA_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "lzma initialization failed(%d)", r); + + return (ARCHIVE_FAILED); + } + + /* Flag the cleanup function that we want our lzma-related structures + * to be freed later. */ + zip->zipx_lzma_valid = 1; + + /* The "lzma alone" file format and the stream format inside ZIPx are + * almost the same. Here's an example of a structure of "lzma alone" + * format: + * + * $ cat /bin/ls | lzma | xxd | head -n 1 + * 00000000: 5d00 0080 00ff ffff ffff ffff ff00 2814 + * + * 5 bytes 8 bytes n bytes + * + * + * lzma_params is a 5-byte blob that has to be decoded to extract + * parameters of this LZMA stream. The uncompressed_size field is an + * uint64_t value that contains information about the size of the + * uncompressed file, or UINT64_MAX if this value is unknown. + * The part is the actual lzma-compressed data stream. + * + * Now here's the structure of the stream inside the ZIPX file: + * + * $ cat stream_inside_zipx | xxd | head -n 1 + * 00000000: 0914 0500 5d00 8000 0000 2814 .... .... + * + * 2byte 2byte 5 bytes n bytes + * + * + * This means that the ZIPX file contains an additional magic1 and + * magic2 headers, the lzma_params field contains the same parameter + * set as in the "lzma alone" format, and the field is the + * same as in the "lzma alone" format as well. Note that also the zipx + * format is missing the uncompressed_size field. + * + * So, in order to use the "lzma alone" decoder for the zipx lzma + * stream, we simply need to shuffle around some fields, prepare a new + * lzma alone header, feed it into lzma alone decoder so it will + * initialize itself properly, and then we can start feeding normal + * zipx lzma stream into the decoder. + */ + + /* Read magic1,magic2,lzma_params from the ZIPX stream. */ + if((p = __archive_read_ahead(a, 9, NULL)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated lzma data"); + return (ARCHIVE_FATAL); + } + + if(p[2] != 0x05 || p[3] != 0x00) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid lzma data"); + return (ARCHIVE_FATAL); + } + + /* Prepare an lzma alone header: copy the lzma_params blob into + * a proper place into the lzma alone header. */ + memcpy(&alone_header.bytes[0], p + 4, 5); + + /* Initialize the 'uncompressed size' field to unknown; we'll manually + * monitor how many bytes there are still to be uncompressed. */ + alone_header.uncompressed_size = UINT64_MAX; + + if(!zip->uncompressed_buffer) { + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for lzma decompression"); + return (ARCHIVE_FATAL); + } + } + + zip->zipx_lzma_stream.next_in = (void*) &alone_header; + zip->zipx_lzma_stream.avail_in = sizeof(alone_header); + zip->zipx_lzma_stream.total_in = 0; + zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; + zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; + zip->zipx_lzma_stream.total_out = 0; + + /* Feed only the header into the lzma alone decoder. This will + * effectively initialize the decoder, and will not produce any + * output bytes yet. */ + r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); + if (r != LZMA_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "lzma stream initialization error"); + return ARCHIVE_FATAL; + } + + /* We've already consumed some bytes, so take this into account. */ + __archive_read_consume(a, 9); + zip->entry_bytes_remaining -= 9; + zip->entry_compressed_bytes_read += 9; + + zip->decompress_init = 1; + return (ARCHIVE_OK); +} + +static int +zip_read_data_zipx_xz(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip* zip = (struct zip *)(a->format->data); + int ret; + lzma_ret lz_ret; + const void* compressed_buf; + ssize_t bytes_avail, in_bytes, to_consume = 0; + + (void) offset; /* UNUSED */ + + /* Initialize decompressor if not yet initialized. */ + if (!zip->decompress_init) { + ret = zipx_xz_init(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + } + + compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); + if (bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated xz file body"); + return (ARCHIVE_FATAL); + } + + in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + zip->zipx_lzma_stream.next_in = compressed_buf; + zip->zipx_lzma_stream.avail_in = in_bytes; + zip->zipx_lzma_stream.total_in = 0; + zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; + zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; + zip->zipx_lzma_stream.total_out = 0; + + /* Perform the decompression. */ + lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); + switch(lz_ret) { + case LZMA_DATA_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "xz data error (error %d)", (int) lz_ret); + return (ARCHIVE_FATAL); + + case LZMA_NO_CHECK: + case LZMA_OK: + break; + + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "xz unknown error %d", (int) lz_ret); + return (ARCHIVE_FATAL); + + case LZMA_STREAM_END: + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + + if((int64_t) zip->zipx_lzma_stream.total_in != + zip->entry_bytes_remaining) + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "xz premature end of stream"); + return (ARCHIVE_FATAL); + } + + zip->end_of_entry = 1; + break; + } + + to_consume = zip->zipx_lzma_stream.total_in; + + __archive_read_consume(a, to_consume); + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; + + *size = zip->zipx_lzma_stream.total_out; + *buff = zip->uncompressed_buffer; + + ret = consume_optional_marker(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + + return (ARCHIVE_OK); +} + +static int +zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip* zip = (struct zip *)(a->format->data); + int ret; + lzma_ret lz_ret; + const void* compressed_buf; + ssize_t bytes_avail, in_bytes, to_consume; + + (void) offset; /* UNUSED */ + + /* Initialize decompressor if not yet initialized. */ + if (!zip->decompress_init) { + ret = zipx_lzma_alone_init(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + } + + /* Fetch more compressed data. The same note as in deflate handler + * applies here as well: + * + * Note: '1' here is a performance optimization. Recall that the + * decompression layer returns a count of available bytes; asking for + * more than that forces the decompressor to combine reads by copying + * data. + */ + compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); + if (bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated lzma file body"); + return (ARCHIVE_FATAL); + } + + /* Set decompressor parameters. */ + in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + + zip->zipx_lzma_stream.next_in = compressed_buf; + zip->zipx_lzma_stream.avail_in = in_bytes; + zip->zipx_lzma_stream.total_in = 0; + zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; + zip->zipx_lzma_stream.avail_out = + /* These lzma_alone streams lack end of stream marker, so let's + * make sure the unpacker won't try to unpack more than it's + * supposed to. */ + zipmin((int64_t) zip->uncompressed_buffer_size, + zip->entry->uncompressed_size - + zip->entry_uncompressed_bytes_read); + zip->zipx_lzma_stream.total_out = 0; + + /* Perform the decompression. */ + lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); + switch(lz_ret) { + case LZMA_DATA_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lzma data error (error %d)", (int) lz_ret); + return (ARCHIVE_FATAL); + + /* This case is optional in lzma alone format. It can happen, + * but most of the files don't have it. (GitHub #1257) */ + case LZMA_STREAM_END: + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + if((int64_t) zip->zipx_lzma_stream.total_in != + zip->entry_bytes_remaining) + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "lzma alone premature end of stream"); + return (ARCHIVE_FATAL); + } + + zip->end_of_entry = 1; + break; + + case LZMA_OK: + break; + + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lzma unknown error %d", (int) lz_ret); + return (ARCHIVE_FATAL); + } + + to_consume = zip->zipx_lzma_stream.total_in; + + /* Update pointers. */ + __archive_read_consume(a, to_consume); + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; + + if(zip->entry_bytes_remaining == 0) { + zip->end_of_entry = 1; + } + + /* Return values. */ + *size = zip->zipx_lzma_stream.total_out; + *buff = zip->uncompressed_buffer; + + /* Behave the same way as during deflate decompression. */ + ret = consume_optional_marker(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + + /* Free lzma decoder handle because we'll no longer need it. */ + if(zip->end_of_entry) { + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + } + + /* If we're here, then we're good! */ + return (ARCHIVE_OK); +} +#endif /* HAVE_LZMA_H && HAVE_LIBLZMA */ + +static int +zipx_ppmd8_init(struct archive_read *a, struct zip *zip) +{ + const void* p; + uint32_t val; + uint32_t order; + uint32_t mem; + uint32_t restore_method; + + /* Remove previous decompression context if it exists. */ + if(zip->ppmd8_valid) { + __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); + zip->ppmd8_valid = 0; + } + + /* Create a new decompression context. */ + __archive_ppmd8_functions.Ppmd8_Construct(&zip->ppmd8); + zip->ppmd8_stream_failed = 0; + + /* Setup function pointers required by Ppmd8 decompressor. The + * 'ppmd_read' function will feed new bytes to the decompressor, + * and will increment the 'zip->zipx_ppmd_read_compressed' counter. */ + zip->ppmd8.Stream.In = &zip->zipx_ppmd_stream; + zip->zipx_ppmd_stream.a = a; + zip->zipx_ppmd_stream.Read = &ppmd_read; + + /* Reset number of read bytes to 0. */ + zip->zipx_ppmd_read_compressed = 0; + + /* Read Ppmd8 header (2 bytes). */ + p = __archive_read_ahead(a, 2, NULL); + if(!p) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated file data in PPMd8 stream"); + return (ARCHIVE_FATAL); + } + __archive_read_consume(a, 2); + + /* Decode the stream's compression parameters. */ + val = archive_le16dec(p); + order = (val & 15) + 1; + mem = ((val >> 4) & 0xff) + 1; + restore_method = (val >> 12); + + if(order < 2 || restore_method > 2) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid parameter set in PPMd8 stream (order=%d, " + "restore=%d)", order, restore_method); + return (ARCHIVE_FAILED); + } + + /* Allocate the memory needed to properly decompress the file. */ + if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) { + archive_set_error(&a->archive, ENOMEM, + "Unable to allocate memory for PPMd8 stream: %d bytes", + mem << 20); + return (ARCHIVE_FATAL); + } + + /* Signal the cleanup function to release Ppmd8 context in the + * cleanup phase. */ + zip->ppmd8_valid = 1; + + /* Perform further Ppmd8 initialization. */ + if(!__archive_ppmd8_functions.Ppmd8_RangeDec_Init(&zip->ppmd8)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "PPMd8 stream range decoder initialization error"); + return (ARCHIVE_FATAL); + } + + __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order, + restore_method); + + /* Allocate the buffer that will hold uncompressed data. */ + free(zip->uncompressed_buffer); + + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + + if(zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for PPMd8 decompression"); + return ARCHIVE_FATAL; + } + + /* Ppmd8 initialization is done. */ + zip->decompress_init = 1; + + /* We've already read 2 bytes in the output stream. Additionally, + * Ppmd8 initialization code could read some data as well. So we + * are advancing the stream by 2 bytes plus whatever number of + * bytes Ppmd8 init function used. */ + zip->entry_compressed_bytes_read += 2 + zip->zipx_ppmd_read_compressed; + + return ARCHIVE_OK; +} + +static int +zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip* zip = (struct zip *)(a->format->data); + int ret; + size_t consumed_bytes = 0; + ssize_t bytes_avail = 0; + + (void) offset; /* UNUSED */ + + /* If we're here for the first time, initialize Ppmd8 decompression + * context first. */ + if(!zip->decompress_init) { + ret = zipx_ppmd8_init(a, zip); + if(ret != ARCHIVE_OK) + return ret; + } + + /* Fetch for more data. We're reading 1 byte here, but libarchive + * should prefetch more bytes. */ + (void) __archive_read_ahead(a, 1, &bytes_avail); + if(bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated PPMd8 file body"); + return (ARCHIVE_FATAL); + } + + /* This counter will be updated inside ppmd_read(), which at one + * point will be called by Ppmd8_DecodeSymbol. */ + zip->zipx_ppmd_read_compressed = 0; + + /* Decompression loop. */ + do { + int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol( + &zip->ppmd8); + if(sym < 0) { + zip->end_of_entry = 1; + break; + } + + /* This field is set by ppmd_read() when there was no more data + * to be read. */ + if(zip->ppmd8_stream_failed) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated PPMd8 file body"); + return (ARCHIVE_FATAL); + } + + zip->uncompressed_buffer[consumed_bytes] = (uint8_t) sym; + ++consumed_bytes; + } while(consumed_bytes < zip->uncompressed_buffer_size); + + /* Update pointers for libarchive. */ + *buff = zip->uncompressed_buffer; + *size = consumed_bytes; + + /* Update pointers so we can continue decompression in another call. */ + zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed; + zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed; + zip->entry_uncompressed_bytes_read += consumed_bytes; + + /* If we're at the end of stream, deinitialize Ppmd8 context. */ + if(zip->end_of_entry) { + __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); + zip->ppmd8_valid = 0; + } + + /* Seek for optional marker, same way as in each zip entry. */ + ret = consume_optional_marker(a, zip); + if (ret != ARCHIVE_OK) + return ret; + + return ARCHIVE_OK; +} + +#ifdef HAVE_BZLIB_H +static int +zipx_bzip2_init(struct archive_read *a, struct zip *zip) +{ + int r; + + /* Deallocate already existing BZ2 decompression context if it + * exists. */ + if(zip->bzstream_valid) { + BZ2_bzDecompressEnd(&zip->bzstream); + zip->bzstream_valid = 0; + } + + /* Allocate a new BZ2 decompression context. */ + memset(&zip->bzstream, 0, sizeof(bz_stream)); + r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1); + if(r != BZ_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "bzip2 initialization failed(%d)", + r); + + return ARCHIVE_FAILED; + } + + /* Mark the bzstream field to be released in cleanup phase. */ + zip->bzstream_valid = 1; + + /* (Re)allocate the buffer that will contain decompressed bytes. */ + free(zip->uncompressed_buffer); + + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for bzip2 decompression"); + return ARCHIVE_FATAL; + } + + /* Initialization done. */ + zip->decompress_init = 1; + return ARCHIVE_OK; +} + +static int +zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip *zip = (struct zip *)(a->format->data); + ssize_t bytes_avail = 0, in_bytes, to_consume; + const void *compressed_buff; + int r; + uint64_t total_out; + + (void) offset; /* UNUSED */ + + /* Initialize decompression context if we're here for the first time. */ + if(!zip->decompress_init) { + r = zipx_bzip2_init(a, zip); + if(r != ARCHIVE_OK) + return r; + } + + /* Fetch more compressed bytes. */ + compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); + if(bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated bzip2 file body"); + return (ARCHIVE_FATAL); + } + + in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + if(in_bytes < 1) { + /* libbz2 doesn't complain when caller feeds avail_in == 0. + * It will actually return success in this case, which is + * undesirable. This is why we need to make this check + * manually. */ + + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated bzip2 file body"); + return (ARCHIVE_FATAL); + } + + /* Setup buffer boundaries. */ + zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff; + zip->bzstream.avail_in = in_bytes; + zip->bzstream.total_in_hi32 = 0; + zip->bzstream.total_in_lo32 = 0; + zip->bzstream.next_out = (char*) zip->uncompressed_buffer; + zip->bzstream.avail_out = zip->uncompressed_buffer_size; + zip->bzstream.total_out_hi32 = 0; + zip->bzstream.total_out_lo32 = 0; + + /* Perform the decompression. */ + r = BZ2_bzDecompress(&zip->bzstream); + switch(r) { + case BZ_STREAM_END: + /* If we're at the end of the stream, deinitialize the + * decompression context now. */ + switch(BZ2_bzDecompressEnd(&zip->bzstream)) { + case BZ_OK: + break; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Failed to clean up bzip2 " + "decompressor"); + return ARCHIVE_FATAL; + } + + zip->end_of_entry = 1; + break; + case BZ_OK: + /* The decompressor has successfully decoded this + * chunk of data, but more data is still in queue. */ + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "bzip2 decompression failed"); + return ARCHIVE_FATAL; + } + + /* Update the pointers so decompressor can continue decoding. */ + to_consume = zip->bzstream.total_in_lo32; + __archive_read_consume(a, to_consume); + + total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) + + zip->bzstream.total_out_lo32; + + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += total_out; + + /* Give libarchive its due. */ + *size = total_out; + *buff = zip->uncompressed_buffer; + + /* Seek for optional marker, like in other entries. */ + r = consume_optional_marker(a, zip); + if(r != ARCHIVE_OK) + return r; + + return ARCHIVE_OK; +} + +#endif + #ifdef HAVE_ZLIB_H static int zip_deflate_init(struct archive_read *a, struct zip *zip) @@ -1371,8 +2252,10 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, if (zip->tctx_valid || zip->cctx_valid) { if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { size_t buff_remaining = - (zip->decrypted_buffer + zip->decrypted_buffer_size) - - (zip->decrypted_ptr + zip->decrypted_bytes_remaining); + (zip->decrypted_buffer + + zip->decrypted_buffer_size) + - (zip->decrypted_ptr + + zip->decrypted_bytes_remaining); if (buff_remaining > (size_t)bytes_avail) buff_remaining = (size_t)bytes_avail; @@ -1383,12 +2266,12 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, + buff_remaining) > zip->entry_bytes_remaining) { if (zip->entry_bytes_remaining < - (int64_t)zip->decrypted_bytes_remaining) + (int64_t)zip->decrypted_bytes_remaining) buff_remaining = 0; else buff_remaining = (size_t)zip->entry_bytes_remaining - - zip->decrypted_bytes_remaining; + - zip->decrypted_bytes_remaining; } } if (buff_remaining > 0) { @@ -1407,7 +2290,8 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, + zip->decrypted_bytes_remaining, &dsize); } - zip->decrypted_bytes_remaining += buff_remaining; + zip->decrypted_bytes_remaining += + buff_remaining; } } bytes_avail = zip->decrypted_bytes_remaining; @@ -1470,42 +2354,9 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, return (r); } - if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { - const char *p; - - if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated ZIP end-of-file record"); - return (ARCHIVE_FATAL); - } - /* Consume the optional PK\007\010 marker. */ - if (p[0] == 'P' && p[1] == 'K' && - p[2] == '\007' && p[3] == '\010') { - p += 4; - zip->unconsumed = 4; - } - if (zip->entry->flags & LA_USED_ZIP64) { - uint64_t compressed, uncompressed; - zip->entry->crc32 = archive_le32dec(p); - compressed = archive_le64dec(p + 4); - uncompressed = archive_le64dec(p + 12); - if (compressed > INT64_MAX || uncompressed > INT64_MAX) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Overflow of 64-bit file sizes"); - return ARCHIVE_FAILED; - } - zip->entry->compressed_size = compressed; - zip->entry->uncompressed_size = uncompressed; - zip->unconsumed += 20; - } else { - zip->entry->crc32 = archive_le32dec(p); - zip->entry->compressed_size = archive_le32dec(p + 4); - zip->entry->uncompressed_size = archive_le32dec(p + 8); - zip->unconsumed += 12; - } - } + r = consume_optional_marker(a, zip); + if (r != ARCHIVE_OK) + return (r); return (ARCHIVE_OK); } @@ -1933,6 +2784,24 @@ archive_read_format_zip_read_data(struct archive_read *a, case 0: /* No compression. */ r = zip_read_data_none(a, buff, size, offset); break; +#ifdef HAVE_BZLIB_H + case 12: /* ZIPx bzip2 compression. */ + r = zip_read_data_zipx_bzip2(a, buff, size, offset); + break; +#endif +#if HAVE_LZMA_H && HAVE_LIBLZMA + case 14: /* ZIPx LZMA compression. */ + r = zip_read_data_zipx_lzma_alone(a, buff, size, offset); + break; + case 95: /* ZIPx XZ compression. */ + r = zip_read_data_zipx_xz(a, buff, size, offset); + break; +#endif + /* PPMd support is built-in, so we don't need any #if guards. */ + case 98: /* ZIPx PPMd compression. */ + r = zip_read_data_zipx_ppmd(a, buff, size, offset); + break; + #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ r = zip_read_data_deflate(a, buff, size, offset); @@ -1941,8 +2810,8 @@ archive_read_format_zip_read_data(struct archive_read *a, default: /* Unsupported compression. */ /* Return a warning. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported ZIP compression method (%s)", - compression_name(zip->entry->compression)); + "Unsupported ZIP compression method (%d: %s)", + zip->entry->compression, compression_name(zip->entry->compression)); /* We can't decompress this entry, but we will * be able to skip() it and try the next entry. */ return (ARCHIVE_FAILED); @@ -2000,11 +2869,29 @@ archive_read_format_zip_cleanup(struct archive_read *a) struct zip_entry *zip_entry, *next_zip_entry; zip = (struct zip *)(a->format->data); + #ifdef HAVE_ZLIB_H if (zip->stream_valid) inflateEnd(&zip->stream); - free(zip->uncompressed_buffer); #endif + +#if HAVE_LZMA_H && HAVE_LIBLZMA + if (zip->zipx_lzma_valid) { + lzma_end(&zip->zipx_lzma_stream); + } +#endif + +#ifdef HAVE_BZLIB_H + if (zip->bzstream_valid) { + BZ2_bzDecompressEnd(&zip->bzstream); + } +#endif + + free(zip->uncompressed_buffer); + + if (zip->ppmd8_valid) + __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); + if (zip->zip_entries) { zip_entry = zip->zip_entries; while (zip_entry != NULL) { @@ -2628,7 +3515,8 @@ expose_parent_dirs(struct zip *zip, const char *name, size_t name_length) } static int -slurp_central_directory(struct archive_read *a, struct zip *zip) +slurp_central_directory(struct archive_read *a, struct archive_entry* entry, + struct zip *zip) { ssize_t i; unsigned found; @@ -2708,6 +3596,11 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) return ARCHIVE_FATAL; zip_entry = calloc(1, sizeof(struct zip_entry)); + if (zip_entry == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate zip entry"); + return ARCHIVE_FATAL; + } zip_entry->next = zip->zip_entries; zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY; zip->zip_entries = zip_entry; @@ -2733,8 +3626,10 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) filename_length = archive_le16dec(p + 28); extra_length = archive_le16dec(p + 30); comment_length = archive_le16dec(p + 32); - /* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */ - /* internal_attributes = archive_le16dec(p + 36); */ /* text bit */ + /* disk_start = archive_le16dec(p + 34); + * Better be zero. + * internal_attributes = archive_le16dec(p + 36); + * text bit */ external_attributes = archive_le32dec(p + 38); zip_entry->local_header_offset = archive_le32dec(p + 42) + correction; @@ -2770,7 +3665,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) "Truncated ZIP file header"); return ARCHIVE_FATAL; } - if (ARCHIVE_OK != process_extra(a, p + filename_length, extra_length, zip_entry)) { + if (ARCHIVE_OK != process_extra(a, entry, p + filename_length, + extra_length, zip_entry)) { return ARCHIVE_FATAL; } @@ -2792,7 +3688,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) * a directory. We should treat it as a non * resource fork file to expose it. */ if (name[filename_length-1] != '/' && - (r - name < 3 || r[0] != '.' || r[1] != '_')) { + (r - name < 3 || r[0] != '.' || + r[1] != '_')) { __archive_rb_tree_insert_node( &zip->tree, &zip_entry->node); /* Expose its parent directories. */ @@ -2869,8 +3766,10 @@ zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, switch(rsrc->compression) { case 0: /* No compression. */ if (rsrc->uncompressed_size != rsrc->compressed_size) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed OS X metadata entry: inconsistent size"); + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed OS X metadata entry: " + "inconsistent size"); return (ARCHIVE_FATAL); } #ifdef HAVE_ZLIB_H @@ -3029,7 +3928,7 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, a->archive.archive_format_name = "ZIP"; if (zip->zip_entries == NULL) { - r = slurp_central_directory(a, zip); + r = slurp_central_directory(a, entry, zip); if (r != ARCHIVE_OK) return r; /* Get first entry whose local header offset is lower than @@ -3059,8 +3958,8 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, __archive_read_reset_passphrase(a); /* File entries are sorted by the header offset, we should mostly - * use __archive_read_consume to advance a read point to avoid redundant - * data reading. */ + * use __archive_read_consume to advance a read point to avoid + * redundant data reading. */ offset = archive_filter_bytes(&a->archive, 0); if (offset < zip->entry->local_header_offset) __archive_read_consume(a, @@ -3141,3 +4040,5 @@ archive_read_support_format_zip_seekable(struct archive *_a) free(zip); return (ARCHIVE_OK); } + +/*# vim:set noet:*/ diff --git a/Utilities/cmlibarchive/libarchive/archive_string.c b/Utilities/cmlibarchive/libarchive/archive_string.c index 85594cc9e59..c77dcf52c25 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string.c +++ b/Utilities/cmlibarchive/libarchive/archive_string.c @@ -75,6 +75,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33 #define wmemmove(a,b,i) (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t)) #endif +#undef max +#define max(a, b) ((a)>(b)?(a):(b)) + struct archive_string_conv { struct archive_string_conv *next; char *from_charset; @@ -458,7 +461,7 @@ archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest, if (from_cp == CP_C_LOCALE) { /* - * "C" locale special process. + * "C" locale special processing. */ wchar_t *ws; const unsigned char *mp; @@ -591,7 +594,7 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest, * No single byte will be more than one wide character, * so this length estimate will always be big enough. */ - size_t wcs_length = len; + // size_t wcs_length = len; size_t mbs_length = len; const char *mbs = p; wchar_t *wcs; @@ -600,7 +603,11 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest, memset(&shift_state, 0, sizeof(shift_state)); #endif - if (NULL == archive_wstring_ensure(dest, dest->length + wcs_length + 1)) + /* + * As we decided to have wcs_length == mbs_length == len + * we can use len here instead of wcs_length + */ + if (NULL == archive_wstring_ensure(dest, dest->length + len + 1)) return (-1); wcs = dest->s + dest->length; /* @@ -609,6 +616,12 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest, * multi bytes. */ while (*mbs && mbs_length > 0) { + /* + * The buffer we allocated is always big enough. + * Keep this code path in a comment if we decide to choose + * smaller wcs_length in the future + */ +/* if (wcs_length == 0) { dest->length = wcs - dest->s; dest->s[dest->length] = L'\0'; @@ -618,24 +631,20 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest, return (-1); wcs = dest->s + dest->length; } +*/ #if HAVE_MBRTOWC - r = mbrtowc(wcs, mbs, wcs_length, &shift_state); + r = mbrtowc(wcs, mbs, mbs_length, &shift_state); #else - r = mbtowc(wcs, mbs, wcs_length); + r = mbtowc(wcs, mbs, mbs_length); #endif if (r == (size_t)-1 || r == (size_t)-2) { ret_val = -1; - if (errno == EILSEQ) { - ++mbs; - --mbs_length; - continue; - } else - break; + break; } if (r == 0 || r > mbs_length) break; wcs++; - wcs_length--; + // wcs_length--; mbs += r; mbs_length -= r; } @@ -680,7 +689,7 @@ archive_string_append_from_wcs_in_codepage(struct archive_string *as, if (to_cp == CP_C_LOCALE) { /* - * "C" locale special process. + * "C" locale special processing. */ const wchar_t *wp = ws; char *p; @@ -799,7 +808,8 @@ archive_string_append_from_wcs(struct archive_string *as, as->s[as->length] = '\0'; /* Re-allocate buffer for MBS. */ if (archive_string_ensure(as, - as->length + len * 2 + 1) == NULL) + as->length + max(len * 2, + (size_t)MB_CUR_MAX) + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; @@ -890,7 +900,7 @@ add_converter(struct archive_string_conv *sc, int (*converter) struct archive_string_conv *)) { if (sc == NULL || sc->nconverter >= 2) - __archive_errx(1, "Programing error"); + __archive_errx(1, "Programming error"); sc->converter[sc->nconverter++] = converter; } @@ -3441,7 +3451,8 @@ strncat_from_utf8_libarchive2(struct archive_string *as, as->length = p - as->s; /* Re-allocate buffer for MBS. */ if (archive_string_ensure(as, - as->length + len * 2 + 1) == NULL) + as->length + max(len * 2, + (size_t)MB_CUR_MAX) + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; @@ -4053,6 +4064,7 @@ archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8) { if (utf8 == NULL) { aes->aes_set = 0; + return (0); } aes->aes_set = AES_SET_UTF8; archive_string_empty(&(aes->aes_mbs)); @@ -4067,6 +4079,7 @@ archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs, { if (wcs == NULL) { aes->aes_set = 0; + return (0); } aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ archive_string_empty(&(aes->aes_mbs)); diff --git a/Utilities/cmlibarchive/libarchive/archive_string.h b/Utilities/cmlibarchive/libarchive/archive_string.h index 56dfbb28f28..27e1ad69c56 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string.h +++ b/Utilities/cmlibarchive/libarchive/archive_string.h @@ -26,15 +26,15 @@ * */ +#ifndef ARCHIVE_STRING_H_INCLUDED +#define ARCHIVE_STRING_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif #endif -#ifndef ARCHIVE_STRING_H_INCLUDED -#define ARCHIVE_STRING_H_INCLUDED - #include #ifdef HAVE_STDLIB_H #include /* required for wchar_t on some systems */ diff --git a/Utilities/cmlibarchive/libarchive/archive_string_composition.h b/Utilities/cmlibarchive/libarchive/archive_string_composition.h index 8902ac1f7f3..d0ac340961a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string_composition.h +++ b/Utilities/cmlibarchive/libarchive/archive_string_composition.h @@ -34,13 +34,13 @@ * See also http://unicode.org/report/tr15/ */ +#ifndef ARCHIVE_STRING_COMPOSITION_H_INCLUDED +#define ARCHIVE_STRING_COMPOSITION_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_STRING_COMPOSITION_H_INCLUDED -#define ARCHIVE_STRING_COMPOSITION_H_INCLUDED - struct unicode_composition_table { uint32_t cp1; uint32_t cp2; diff --git a/Utilities/cmlibarchive/libarchive/archive_util.3 b/Utilities/cmlibarchive/libarchive/archive_util.3 index 99ab842a28c..d5d4e7dfd7d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_util.3 +++ b/Utilities/cmlibarchive/libarchive/archive_util.3 @@ -92,10 +92,10 @@ Clears any error information left over from a previous call. Not generally used in client code. .It Fn archive_compression Synonym for -.Fn archive_filter_code(a, 0) . +.Fn archive_filter_code a 0 . .It Fn archive_compression_name Synonym for -.Fn archive_filter_name(a, 0) . +.Fn archive_filter_name a 0 . .It Fn archive_copy_error Copies error information from one archive to another. .It Fn archive_errno @@ -142,13 +142,13 @@ filter 0 is the gunzip filter, filter 1 is the uudecode filter, and filter 2 is the pseudo-filter that wraps the archive read functions. In this case, requesting -.Fn archive_position(a, -1) +.Fn archive_position a -1 would be a synonym for -.Fn archive_position(a, 2) +.Fn archive_position a 2 which would return the number of bytes currently read from the archive, while -.Fn archive_position(a, 1) +.Fn archive_position a 1 would return the number of bytes after uudecoding, and -.Fn archive_position(a, 0) +.Fn archive_position a 0 would return the number of bytes after decompression. .It Fn archive_filter_name Returns a textual name identifying the indicated filter. @@ -170,9 +170,9 @@ A textual description of the format of the current entry. .It Fn archive_position Returns the number of bytes read from or written to the indicated filter. In particular, -.Fn archive_position(a, 0) +.Fn archive_position a 0 returns the number of bytes read or written by the format handler, while -.Fn archive_position(a, -1) +.Fn archive_position a -1 returns the number of bytes read or written to the archive. See .Fn archive_filter_count diff --git a/Utilities/cmlibarchive/libarchive/archive_util.c b/Utilities/cmlibarchive/libarchive/archive_util.c index e5c6e3bffa5..10dca73c651 100644 --- a/Utilities/cmlibarchive/libarchive/archive_util.c +++ b/Utilities/cmlibarchive/libarchive/archive_util.c @@ -46,13 +46,13 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1 #include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #ifdef HAVE_LZMA_H -#include +#include #endif #ifdef HAVE_BZLIB_H -#include +#include #endif #ifdef HAVE_LZ4_H #include @@ -218,8 +218,8 @@ __archive_errx(int retvalue, const char *msg) * Also Windows version of mktemp family including _mktemp_s * are not secure. */ -int -__archive_mktemp(const char *tmpdir) +static int +__archive_mktempx(const char *tmpdir, wchar_t *template) { static const wchar_t prefix[] = L"libarchive_"; static const wchar_t suffix[] = L"XXXXXXXXXX"; @@ -243,64 +243,76 @@ __archive_mktemp(const char *tmpdir) hProv = (HCRYPTPROV)NULL; fd = -1; ws = NULL; - archive_string_init(&temp_name); - /* Get a temporary directory. */ - if (tmpdir == NULL) { - size_t l; - wchar_t *tmp; + if (template == NULL) { + archive_string_init(&temp_name); - l = GetTempPathW(0, NULL); - if (l == 0) { - la_dosmaperr(GetLastError()); - goto exit_tmpfile; - } - tmp = malloc(l*sizeof(wchar_t)); - if (tmp == NULL) { - errno = ENOMEM; - goto exit_tmpfile; - } - GetTempPathW((DWORD)l, tmp); - archive_wstrcpy(&temp_name, tmp); - free(tmp); - } else { - if (archive_wstring_append_from_mbs(&temp_name, tmpdir, - strlen(tmpdir)) < 0) - goto exit_tmpfile; - if (temp_name.s[temp_name.length-1] != L'/') - archive_wstrappend_wchar(&temp_name, L'/'); - } + /* Get a temporary directory. */ + if (tmpdir == NULL) { + size_t l; + wchar_t *tmp; - /* Check if temp_name is a directory. */ - attr = GetFileAttributesW(temp_name.s); - if (attr == (DWORD)-1) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) { - la_dosmaperr(GetLastError()); - goto exit_tmpfile; - } - ws = __la_win_permissive_name_w(temp_name.s); - if (ws == NULL) { - errno = EINVAL; - goto exit_tmpfile; + l = GetTempPathW(0, NULL); + if (l == 0) { + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } + tmp = malloc(l*sizeof(wchar_t)); + if (tmp == NULL) { + errno = ENOMEM; + goto exit_tmpfile; + } + GetTempPathW((DWORD)l, tmp); + archive_wstrcpy(&temp_name, tmp); + free(tmp); + } else { + if (archive_wstring_append_from_mbs(&temp_name, tmpdir, + strlen(tmpdir)) < 0) + goto exit_tmpfile; + if (temp_name.s[temp_name.length-1] != L'/') + archive_wstrappend_wchar(&temp_name, L'/'); } - attr = GetFileAttributesW(ws); + + /* Check if temp_name is a directory. */ + attr = GetFileAttributesW(temp_name.s); if (attr == (DWORD)-1) { - la_dosmaperr(GetLastError()); + if (GetLastError() != ERROR_FILE_NOT_FOUND) { + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } + ws = __la_win_permissive_name_w(temp_name.s); + if (ws == NULL) { + errno = EINVAL; + goto exit_tmpfile; + } + attr = GetFileAttributesW(ws); + if (attr == (DWORD)-1) { + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } + } + if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { + errno = ENOTDIR; goto exit_tmpfile; } - } - if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { - errno = ENOTDIR; - goto exit_tmpfile; - } - /* - * Create a temporary file. - */ - archive_wstrcat(&temp_name, prefix); - archive_wstrcat(&temp_name, suffix); - ep = temp_name.s + archive_strlen(&temp_name); - xp = ep - wcslen(suffix); + /* + * Create a temporary file. + */ + archive_wstrcat(&temp_name, prefix); + archive_wstrcat(&temp_name, suffix); + ep = temp_name.s + archive_strlen(&temp_name); + xp = ep - wcslen(suffix); + template = temp_name.s; + } else { + xp = wcschr(template, L'X'); + if (xp == NULL) /* No X, programming error */ + abort(); + for (ep = xp; *ep == L'X'; ep++) + continue; + if (*ep) /* X followed by non X, programming error */ + abort(); + } if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { @@ -323,20 +335,24 @@ __archive_mktemp(const char *tmpdir) *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))]; free(ws); - ws = __la_win_permissive_name_w(temp_name.s); + ws = __la_win_permissive_name_w(template); if (ws == NULL) { errno = EINVAL; goto exit_tmpfile; } - /* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to - * delete this temporary file immediately when this - * file closed. */ + if (template == temp_name.s) { + attr = FILE_ATTRIBUTE_TEMPORARY | + FILE_FLAG_DELETE_ON_CLOSE; + } else { + /* mkstemp */ + attr = FILE_ATTRIBUTE_NORMAL; + } h = CreateFileW(ws, GENERIC_READ | GENERIC_WRITE | DELETE, 0,/* Not share */ NULL, CREATE_NEW,/* Create a new file only */ - FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, + attr, NULL); if (h == INVALID_HANDLE_VALUE) { /* The same file already exists. retry with @@ -358,10 +374,23 @@ __archive_mktemp(const char *tmpdir) if (hProv != (HCRYPTPROV)NULL) CryptReleaseContext(hProv, 0); free(ws); - archive_wstring_free(&temp_name); + if (template == temp_name.s) + archive_wstring_free(&temp_name); return (fd); } +int +__archive_mktemp(const char *tmpdir) +{ + return __archive_mktempx(tmpdir, NULL); +} + +int +__archive_mkstemp(wchar_t *template) +{ + return __archive_mktempx(NULL, template); +} + #else static int @@ -414,14 +443,24 @@ __archive_mktemp(const char *tmpdir) return (fd); } -#else +int +__archive_mkstemp(char *template) +{ + int fd = -1; + fd = mkstemp(template); + if (fd >= 0) + __archive_ensure_cloexec_flag(fd); + return (fd); +} + +#else /* !HAVE_MKSTEMP */ /* * We use a private routine. */ -int -__archive_mktemp(const char *tmpdir) +static int +__archive_mktempx(const char *tmpdir, char *template) { static const char num[] = { '0', '1', '2', '3', '4', '5', '6', '7', @@ -439,26 +478,37 @@ __archive_mktemp(const char *tmpdir) char *tp, *ep; fd = -1; - archive_string_init(&temp_name); - if (tmpdir == NULL) { - if (get_tempdir(&temp_name) != ARCHIVE_OK) + if (template == NULL) { + archive_string_init(&temp_name); + if (tmpdir == NULL) { + if (get_tempdir(&temp_name) != ARCHIVE_OK) + goto exit_tmpfile; + } else + archive_strcpy(&temp_name, tmpdir); + if (temp_name.s[temp_name.length-1] == '/') { + temp_name.s[temp_name.length-1] = '\0'; + temp_name.length --; + } + if (la_stat(temp_name.s, &st) < 0) goto exit_tmpfile; - } else - archive_strcpy(&temp_name, tmpdir); - if (temp_name.s[temp_name.length-1] == '/') { - temp_name.s[temp_name.length-1] = '\0'; - temp_name.length --; - } - if (stat(temp_name.s, &st) < 0) - goto exit_tmpfile; - if (!S_ISDIR(st.st_mode)) { - errno = ENOTDIR; - goto exit_tmpfile; + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + goto exit_tmpfile; + } + archive_strcat(&temp_name, "/libarchive_"); + tp = temp_name.s + archive_strlen(&temp_name); + archive_strcat(&temp_name, "XXXXXXXXXX"); + ep = temp_name.s + archive_strlen(&temp_name); + template = temp_name.s; + } else { + tp = strchr(template, 'X'); + if (tp == NULL) /* No X, programming error */ + abort(); + for (ep = tp; *ep == 'X'; ep++) + continue; + if (*ep) /* X followed by non X, programming error */ + abort(); } - archive_strcat(&temp_name, "/libarchive_"); - tp = temp_name.s + archive_strlen(&temp_name); - archive_strcat(&temp_name, "XXXXXXXXXX"); - ep = temp_name.s + archive_strlen(&temp_name); do { char *p; @@ -469,19 +519,33 @@ __archive_mktemp(const char *tmpdir) int d = *((unsigned char *)p) % sizeof(num); *p++ = num[d]; } - fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, + fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600); } while (fd < 0 && errno == EEXIST); if (fd < 0) goto exit_tmpfile; __archive_ensure_cloexec_flag(fd); - unlink(temp_name.s); + if (template == temp_name.s) + unlink(temp_name.s); exit_tmpfile: - archive_string_free(&temp_name); + if (template == temp_name.s) + archive_string_free(&temp_name); return (fd); } -#endif /* HAVE_MKSTEMP */ +int +__archive_mktemp(const char *tmpdir) +{ + return __archive_mktempx(tmpdir, NULL); +} + +int +__archive_mkstemp(char *template) +{ + return __archive_mktempx(NULL, template); +} + +#endif /* !HAVE_MKSTEMP */ #endif /* !_WIN32 || __CYGWIN__ */ /* diff --git a/Utilities/cmlibarchive/libarchive/archive_version_details.c b/Utilities/cmlibarchive/libarchive/archive_version_details.c index b9af6d781a4..5f5a5b743c7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_version_details.c +++ b/Utilities/cmlibarchive/libarchive/archive_version_details.c @@ -34,19 +34,19 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1 #include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #ifdef HAVE_LZMA_H -#include +#include #endif #ifdef HAVE_BZLIB_H -#include +#include #endif #ifdef HAVE_LZ4_H #include #endif #ifdef HAVE_ZSTD_H -#include +#include #endif #include "archive.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_windows.c b/Utilities/cmlibarchive/libarchive/archive_windows.c index 6ff8749ae74..624e270095d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_windows.c @@ -445,7 +445,8 @@ fileTimeToUTC(const FILETIME *filetime, time_t *t, long *ns) * Windows' stat() does not accept the path added "\\?\" especially "?" * character. * It means we cannot access the long name path longer than MAX_PATH. - * So I've implemented simular Windows' stat() to access the long name path. + * So I've implemented a function similar to Windows' stat() to access the + * long name path. * And I've added some feature. * 1. set st_ino by nFileIndexHigh and nFileIndexLow of * BY_HANDLE_FILE_INFORMATION. diff --git a/Utilities/cmlibarchive/libarchive/archive_windows.h b/Utilities/cmlibarchive/libarchive/archive_windows.h index c3aed0c90ac..dda63b874da 100644 --- a/Utilities/cmlibarchive/libarchive/archive_windows.h +++ b/Utilities/cmlibarchive/libarchive/archive_windows.h @@ -27,10 +27,6 @@ * $FreeBSD$ */ -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - /* * TODO: A lot of stuff in here isn't actually used by libarchive and * can be trimmed out. Note that this file is used by libarchive and @@ -48,6 +44,10 @@ #ifndef LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED #define LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + /* Start of configuration for native Win32 */ #ifndef MINGW_HAS_SECURE_API #define MINGW_HAS_SECURE_API 1 @@ -117,10 +117,7 @@ #if !defined(__BORLANDC__) && !defined(__WATCOMC__) #define setmode _setmode #endif -#ifdef stat -#undef stat -#endif -#define stat(path,stref) __la_stat(path,stref) +#define la_stat(path,stref) __la_stat(path,stref) #if !defined(__WATCOMC__) #if !defined(__BORLANDC__) #define strdup _strdup diff --git a/Utilities/cmlibarchive/libarchive/archive_write.3 b/Utilities/cmlibarchive/libarchive/archive_write.3 index c1164f5b5fd..e7f7f1384ee 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write.3 @@ -118,7 +118,7 @@ After all entries have been written, use the .Fn archive_write_free function to release all resources. .\" -.Sh EXAMPLE +.Sh EXAMPLES The following sketch illustrates basic usage of the library. In this example, the callback functions are simply wrappers around the standard @@ -192,7 +192,7 @@ write_archive(const char *outname, const char **filename) if (archive_write_set_format_filter_by_ext(a, outname) != ARCHIVE_OK) { archive_write_add_filter_gzip(a); archive_write_set_format_ustar(a); - } + } archive_write_open(a, mydata, myopen, mywrite, myclose); while (*filename) { stat(*filename, &st); @@ -225,8 +225,8 @@ int main(int argc, const char **argv) .Ed .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_write.c b/Utilities/cmlibarchive/libarchive/archive_write.c index e8daf530d26..98a55fb2aa0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write.c +++ b/Utilities/cmlibarchive/libarchive/archive_write.c @@ -212,6 +212,7 @@ __archive_write_allocate_filter(struct archive *_a) f = calloc(1, sizeof(*f)); f->archive = _a; + f->state = ARCHIVE_WRITE_FILTER_STATE_NEW; if (a->filter_first == NULL) a->filter_first = f; else @@ -228,6 +229,9 @@ __archive_write_filter(struct archive_write_filter *f, const void *buff, size_t length) { int r; + /* Never write to non-open filters */ + if (f->state != ARCHIVE_WRITE_FILTER_STATE_OPEN) + return(ARCHIVE_FATAL); if (length == 0) return(ARCHIVE_OK); if (f->write == NULL) @@ -240,27 +244,70 @@ __archive_write_filter(struct archive_write_filter *f, } /* - * Open a filter. + * Recursive function for opening the filter chain + * Last filter is opened first */ -int +static int __archive_write_open_filter(struct archive_write_filter *f) { - if (f->open == NULL) + int ret; + + ret = ARCHIVE_OK; + if (f->next_filter != NULL) + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); + if (f->state != ARCHIVE_WRITE_FILTER_STATE_NEW) + return (ARCHIVE_FATAL); + if (f->open == NULL) { + f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN; return (ARCHIVE_OK); - return (f->open)(f); + } + ret = (f->open)(f); + if (ret == ARCHIVE_OK) + f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN; + else + f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL; + return (ret); } /* - * Close a filter. + * Open all filters */ -int -__archive_write_close_filter(struct archive_write_filter *f) +static int +__archive_write_filters_open(struct archive_write *a) { - if (f->close != NULL) - return (f->close)(f); - if (f->next_filter != NULL) - return (__archive_write_close_filter(f->next_filter)); - return (ARCHIVE_OK); + return (__archive_write_open_filter(a->filter_first)); +} + +/* + * Close all filtes + */ +static int +__archive_write_filters_close(struct archive_write *a) +{ + struct archive_write_filter *f; + int ret, ret1; + ret = ARCHIVE_OK; + for (f = a->filter_first; f != NULL; f = f->next_filter) { + /* Do not close filters that are not open */ + if (f->state == ARCHIVE_WRITE_FILTER_STATE_OPEN) { + if (f->close != NULL) { + ret1 = (f->close)(f); + if (ret1 < ret) + ret = ret1; + if (ret1 == ARCHIVE_OK) { + f->state = + ARCHIVE_WRITE_FILTER_STATE_CLOSED; + } else { + f->state = + ARCHIVE_WRITE_FILTER_STATE_FATAL; + } + } else + f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED; + } + } + return (ret); } int @@ -292,6 +339,7 @@ archive_write_client_open(struct archive_write_filter *f) struct archive_none *state; void *buffer; size_t buffer_size; + int ret; f->bytes_per_block = archive_write_get_bytes_per_block(f->archive); f->bytes_in_last_block = @@ -316,7 +364,13 @@ archive_write_client_open(struct archive_write_filter *f) if (a->client_opener == NULL) return (ARCHIVE_OK); - return (a->client_opener(f->archive, a->client_data)); + ret = a->client_opener(f->archive, a->client_data); + if (ret != ARCHIVE_OK) { + free(state->buffer); + free(state); + f->data = NULL; + } + return (ret); } static int @@ -439,8 +493,6 @@ archive_write_client_close(struct archive_write_filter *f) (*a->client_closer)(&a->archive, a->client_data); free(state->buffer); free(state); - /* Clear the close handler myself not to be called again. */ - f->close = NULL; a->client_data = NULL; /* Clear passphrase. */ if (a->passphrase != NULL) { @@ -448,6 +500,8 @@ archive_write_client_close(struct archive_write_filter *f) free(a->passphrase); a->passphrase = NULL; } + /* Clear the close handler myself not to be called again. */ + f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED; return (ret); } @@ -477,9 +531,10 @@ archive_write_open(struct archive *_a, void *client_data, client_filter->write = archive_write_client_write; client_filter->close = archive_write_client_close; - ret = __archive_write_open_filter(a->filter_first); + ret = __archive_write_filters_open(a); if (ret < ARCHIVE_WARN) { - r1 = __archive_write_close_filter(a->filter_first); + r1 = __archive_write_filters_close(a); + __archive_write_filters_free(_a); return (r1 < ret ? r1 : ret); } @@ -521,7 +576,7 @@ _archive_write_close(struct archive *_a) } /* Finish the compression and close the stream. */ - r1 = __archive_write_close_filter(a->filter_first); + r1 = __archive_write_filters_close(a); if (r1 < r) r = r1; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_b64encode.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_b64encode.c index b46b19a0c74..87fdb73ecb0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_b64encode.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_b64encode.c @@ -149,11 +149,6 @@ archive_filter_b64encode_open(struct archive_write_filter *f) { struct private_b64encode *state = (struct private_b64encode *)f->data; size_t bs = 65536, bpb; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of the of bytes @@ -266,7 +261,6 @@ static int archive_filter_b64encode_close(struct archive_write_filter *f) { struct private_b64encode *state = (struct private_b64encode *)f->data; - int ret, ret2; /* Flush remaining bytes. */ if (state->hold_len != 0) @@ -274,12 +268,8 @@ archive_filter_b64encode_close(struct archive_write_filter *f) archive_string_sprintf(&state->encoded_buff, "====\n"); /* Write the last block */ archive_write_set_bytes_in_last_block(f->archive, 1); - ret = __archive_write_filter(f->next_filter, + return __archive_write_filter(f->next_filter, state->encoded_buff.s, archive_strlen(&state->encoded_buff)); - ret2 = __archive_write_close_filter(f->next_filter); - if (ret > ret2) - ret = ret2; - return (ret); } static int diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c index 6bd0d1d626f..0637e961437 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c @@ -39,7 +39,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_bzip2.c 20 #include #endif #ifdef HAVE_BZLIB_H -#include +#include #endif #include "archive.h" @@ -167,10 +167,6 @@ archive_compressor_bzip2_open(struct archive_write_filter *f) struct private_data *data = (struct private_data *)f->data; int ret; - ret = __archive_write_open_filter(f->next_filter); - if (ret != 0) - return (ret); - if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { @@ -262,7 +258,7 @@ static int archive_compressor_bzip2_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret, r1; + int ret; /* Finish compression cycle. */ ret = drive_compressor(f, data, 1); @@ -281,9 +277,7 @@ archive_compressor_bzip2_close(struct archive_write_filter *f) "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } - - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } static int diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c index 26fcef4d42b..d404fae7dba 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c @@ -146,17 +146,12 @@ archive_write_add_filter_compress(struct archive *_a) static int archive_compressor_compress_open(struct archive_write_filter *f) { - int ret; struct private_data *state; size_t bs = 65536, bpb; f->code = ARCHIVE_FILTER_COMPRESS; f->name = "compress"; - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); - state = (struct private_data *)calloc(1, sizeof(*state)); if (state == NULL) { archive_set_error(f->archive, ENOMEM, @@ -426,30 +421,27 @@ static int archive_compressor_compress_close(struct archive_write_filter *f) { struct private_data *state = (struct private_data *)f->data; - int ret, ret2; + int ret; ret = output_code(f, state->cur_code); if (ret != ARCHIVE_OK) - goto cleanup; + return ret; ret = output_flush(f); if (ret != ARCHIVE_OK) - goto cleanup; + return ret; /* Write the last block */ ret = __archive_write_filter(f->next_filter, state->compressed, state->compressed_offset); -cleanup: - ret2 = __archive_write_close_filter(f->next_filter); - if (ret > ret2) - ret = ret2; - free(state->compressed); - free(state); return (ret); } static int archive_compressor_compress_free(struct archive_write_filter *f) { - (void)f; /* UNUSED */ + struct private_data *state = (struct private_data *)f->data; + + free(state->compressed); + free(state); return (ARCHIVE_OK); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c index 986123ad8b3..3e26605ec1b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c @@ -38,7 +38,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_gzip.c 201 #endif #include #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" @@ -184,10 +184,6 @@ archive_compressor_gzip_open(struct archive_write_filter *f) struct private_data *data = (struct private_data *)f->data; int ret; - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); - if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { @@ -307,7 +303,7 @@ archive_compressor_gzip_close(struct archive_write_filter *f) { unsigned char trailer[8]; struct private_data *data = (struct private_data *)f->data; - int ret, r1; + int ret; /* Finish compression cycle */ ret = drive_compressor(f, data, 1); @@ -338,8 +334,7 @@ archive_compressor_gzip_close(struct archive_write_filter *f) "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } /* diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c index 15fd494a419..cf19fadd563 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c @@ -223,16 +223,11 @@ static int archive_filter_lz4_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret; size_t required_size; static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, 4 * 1024 * 1024 }; size_t pre_block_size; - ret = __archive_write_open_filter(f->next_filter); - if (ret != 0) - return (ret); - if (data->block_maximum_size < 4) data->block_size = bkmap[0]; else @@ -343,7 +338,7 @@ static int archive_filter_lz4_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret, r1; + int ret; /* Finish compression cycle. */ ret = (int)lz4_write_one_block(f, NULL, 0); @@ -366,9 +361,7 @@ archive_filter_lz4_close(struct archive_write_filter *f) ret = __archive_write_filter(f->next_filter, data->out_buffer, data->out - data->out_buffer); } - - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } static int diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lzop.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lzop.c index ad705c4a068..3bd9062e4d3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lzop.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lzop.c @@ -228,11 +228,6 @@ static int archive_write_lzop_open(struct archive_write_filter *f) { struct write_lzop *data = (struct write_lzop *)f->data; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); switch (data->compression_level) { case 1: @@ -439,10 +434,7 @@ archive_write_lzop_close(struct archive_write_filter *f) } /* Write a zero uncompressed size as the end mark of the series of * compressed block. */ - r = __archive_write_filter(f->next_filter, &endmark, sizeof(endmark)); - if (r != ARCHIVE_OK) - return (r); - return (__archive_write_close_filter(f->next_filter)); + return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark)); } #else diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c index 660f693f29d..a4bc1d90eda 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c @@ -212,11 +212,6 @@ __archive_write_program_open(struct archive_write_filter *f, struct archive_write_program_data *data, const char *cmd) { pid_t child; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); if (data->child_buf == NULL) { data->child_buf_len = 65536; @@ -353,11 +348,11 @@ int __archive_write_program_close(struct archive_write_filter *f, struct archive_write_program_data *data) { - int ret, r1, status; + int ret, status; ssize_t bytes_read; if (data->child == 0) - return __archive_write_close_filter(f->next_filter); + return ARCHIVE_OK; ret = 0; close(data->child_stdin); @@ -409,7 +404,6 @@ __archive_write_program_close(struct archive_write_filter *f, "Error closing program: %s", data->program_name); ret = ARCHIVE_FATAL; } - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_uuencode.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_uuencode.c index 23d9c150d17..1ad45892192 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_uuencode.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_uuencode.c @@ -138,11 +138,6 @@ archive_filter_uuencode_open(struct archive_write_filter *f) { struct private_uuencode *state = (struct private_uuencode *)f->data; size_t bs = 65536, bpb; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of the of bytes @@ -257,7 +252,6 @@ static int archive_filter_uuencode_close(struct archive_write_filter *f) { struct private_uuencode *state = (struct private_uuencode *)f->data; - int ret, ret2; /* Flush remaining bytes. */ if (state->hold_len != 0) @@ -265,12 +259,8 @@ archive_filter_uuencode_close(struct archive_write_filter *f) archive_string_sprintf(&state->encoded_buff, "`\nend\n"); /* Write the last block */ archive_write_set_bytes_in_last_block(f->archive, 1); - ret = __archive_write_filter(f->next_filter, + return __archive_write_filter(f->next_filter, state->encoded_buff.s, archive_strlen(&state->encoded_buff)); - ret2 = __archive_write_close_filter(f->next_filter); - if (ret > ret2) - ret = ret2; - return (ret); } static int diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c index e4a35359161..d567ac99570 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c @@ -39,7 +39,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_xz.c 20110 #endif #include #ifdef HAVE_LZMA_H -#include +#include #endif #include "archive.h" @@ -309,10 +309,6 @@ archive_compressor_xz_open(struct archive_write_filter *f) struct private_data *data = f->data; int ret; - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); - if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { @@ -390,10 +386,13 @@ archive_compressor_xz_options(struct archive_write_filter *f, data->compression_level = 6; return (ARCHIVE_OK); } else if (strcmp(key, "threads") == 0) { + char *endptr; + if (value == NULL) return (ARCHIVE_WARN); - data->threads = (int)strtoul(value, NULL, 10); - if (data->threads == 0 && errno != 0) { + errno = 0; + data->threads = (int)strtoul(value, &endptr, 10); + if (errno != 0 || *endptr != '\0') { data->threads = 1; return (ARCHIVE_WARN); } @@ -445,7 +444,7 @@ static int archive_compressor_xz_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret, r1; + int ret; ret = drive_compressor(f, data, 1); if (ret == ARCHIVE_OK) { @@ -463,8 +462,7 @@ archive_compressor_xz_close(struct archive_write_filter *f) } } lzma_end(&(data->stream)); - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } static int diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c index 251b17da580..f67b0256df6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c @@ -38,7 +38,7 @@ __FBSDID("$FreeBSD$"); #include #endif #ifdef HAVE_ZSTD_H -#include +#include #endif #include "archive.h" @@ -172,11 +172,6 @@ static int archive_compressor_zstd_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); if (data->out.dst == NULL) { size_t bs = ZSTD_CStreamOutSize(), bpb; @@ -238,14 +233,9 @@ static int archive_compressor_zstd_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int r1, r2; /* Finish zstd frame */ - r1 = drive_compressor(f, data, 1, NULL, 0); - - r2 = __archive_write_close_filter(f->next_filter); - - return r1 < r2 ? r1 : r2; + return drive_compressor(f, data, 1, NULL, 0); } /* diff --git a/Utilities/cmlibarchive/libarchive/archive_write_blocksize.3 b/Utilities/cmlibarchive/libarchive/archive_write_blocksize.3 index afd84ea4d39..4973f999056 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_blocksize.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_blocksize.3 @@ -107,8 +107,8 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_write_data.3 b/Utilities/cmlibarchive/libarchive/archive_write_data.3 index 9c16cd9b4f7..bc208b45d53 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_data.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_data.3 @@ -82,9 +82,9 @@ and consider any non-negative value as success. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write_finish_entry 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk.3 b/Utilities/cmlibarchive/libarchive/archive_write_disk.3 index 949c9ef106f..2fa016e4547 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 3, 2017 +.Dd January 19, 2020 .Dt ARCHIVE_WRITE_DISK 3 .Os .Sh NAME @@ -113,7 +113,8 @@ or .Pq FreeBSD, Mac OS X for more information on file attributes. .It Cm ARCHIVE_EXTRACT_MAC_METADATA -Mac OS X specific. Restore metadata using +Mac OS X specific. +Restore metadata using .Xr copyfile 3 . By default, .Xr copyfile 3 @@ -138,6 +139,11 @@ is not specified, then SUID and SGID bits will only be restored if the default user and group IDs of newly-created objects on disk happen to match those specified in the archive entry. By default, only basic permissions are restored, and umask is obeyed. +.It Cm ARCHIVE_EXTRACT_SAFE_WRITES +Extract files atomically, by first creating a unique temporary file and then +renaming it to its required destination name. +This avoids a race where an application might see a partial file (or no +file) during extraction. .It Cm ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS Refuse to extract an absolute path. The default is to not refuse such paths. @@ -264,9 +270,9 @@ and functions. .\" .Sh SEE ALSO +.Xr tar 1 , .Xr archive_read 3 , .Xr archive_write 3 , -.Xr tar 1 , .Xr libarchive 3 .Sh HISTORY The diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c index affa5038161..c4be9b0b25d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c @@ -131,7 +131,7 @@ __FBSDID("$FreeBSD$"); #endif #ifdef HAVE_ZLIB_H -#include +#include #endif /* TODO: Support Mac OS 'quarantine' feature. This is really just a @@ -165,6 +165,10 @@ __FBSDID("$FreeBSD$"); #define O_NOFOLLOW 0 #endif +#ifndef AT_FDCWD +#define AT_FDCWD -100 +#endif + struct fixup_entry { struct fixup_entry *next; struct archive_acl acl; @@ -249,6 +253,8 @@ struct archive_write_disk { struct archive_entry *entry; /* Entry being extracted. */ char *name; /* Name of entry, possibly edited. */ struct archive_string _name_data; /* backing store for 'name' */ + char *tmpname; /* Temporary name * */ + struct archive_string _tmpname_data; /* backing store for 'tmpname' */ /* Tasks remaining for this object. */ int todo; /* Tasks deferred until end-of-archive. */ @@ -348,6 +354,9 @@ struct archive_write_disk { #define HFS_BLOCKS(s) ((s) >> 12) + +static int la_opendirat(int, const char *); +static int la_mktemp(struct archive_write_disk *); static void fsobj_error(int *, struct archive_string *, int, const char *, const char *); static int check_symlinks_fsobj(char *, int *, struct archive_string *, @@ -400,6 +409,61 @@ static ssize_t _archive_write_disk_data(struct archive *, const void *, static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t); +static int +la_mktemp(struct archive_write_disk *a) +{ + int oerrno, fd; + mode_t mode; + + archive_string_empty(&a->_tmpname_data); + archive_string_sprintf(&a->_tmpname_data, "%s.XXXXXX", a->name); + a->tmpname = a->_tmpname_data.s; + + fd = __archive_mkstemp(a->tmpname); + if (fd == -1) + return -1; + + mode = a->mode & 0777 & ~a->user_umask; + if (fchmod(fd, mode) == -1) { + oerrno = errno; + close(fd); + errno = oerrno; + return -1; + } + return fd; +} + +static int +la_opendirat(int fd, const char *path) { + const int flags = O_CLOEXEC +#if defined(O_BINARY) + | O_BINARY +#endif +#if defined(O_DIRECTORY) + | O_DIRECTORY +#endif +#if defined(O_PATH) + | O_PATH +#elif defined(O_SEARCH) + | O_SEARCH +#elif defined(__FreeBSD__) && defined(O_EXEC) + | O_EXEC +#else + | O_RDONLY +#endif + ; + +#if !defined(HAVE_OPENAT) + if (fd != AT_FDCWD) { + errno = ENOTSUP; + return (-1); + } else + return (open(path, flags)); +#else + return (openat(fd, path, flags)); +#endif +} + static int lazy_stat(struct archive_write_disk *a) { @@ -1704,6 +1768,20 @@ _archive_write_disk_finish_entry(struct archive *_a) if (r2 < ret) ret = r2; } + /* + * HYPOTHESIS: + * If we're not root, we won't be setting any security + * attributes that may be wiped by the set_mode() routine + * below. We also can't set xattr on non-owner-writable files, + * which may be the state after set_mode(). Perform + * set_xattrs() first based on these constraints. + */ + if (a->user_uid != 0 && + (a->todo & TODO_XATTR)) { + int r2 = set_xattrs(a); + if (r2 < ret) ret = r2; + } + /* * set_mode must precede ACLs on systems such as Solaris and * FreeBSD where setting the mode implicitly clears extended ACLs @@ -1717,8 +1795,10 @@ _archive_write_disk_finish_entry(struct archive *_a) * Security-related extended attributes (such as * security.capability on Linux) have to be restored last, * since they're implicitly removed by other file changes. + * We do this last only when root. */ - if (a->todo & TODO_XATTR) { + if (a->user_uid == 0 && + (a->todo & TODO_XATTR)) { int r2 = set_xattrs(a); if (r2 < ret) ret = r2; } @@ -1773,12 +1853,18 @@ _archive_write_disk_finish_entry(struct archive *_a) if (a->fd >= 0) { close(a->fd); a->fd = -1; + if (a->tmpname) { + if (rename(a->tmpname, a->name) == -1) { + archive_set_error(&a->archive, errno, + "rename failed"); + ret = ARCHIVE_FATAL; + } + a->tmpname = NULL; + } } /* If there's an entry, we can release it now. */ - if (a->entry) { - archive_entry_free(a->entry); - a->entry = NULL; - } + archive_entry_free(a->entry); + a->entry = NULL; a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } @@ -1895,7 +1981,7 @@ edit_deep_directories(struct archive_write_disk *a) return; /* Try to record our starting dir. */ - a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); + a->restore_pwd = la_opendirat(AT_FDCWD, "."); __archive_ensure_cloexec_flag(a->restore_pwd); if (a->restore_pwd < 0) return; @@ -2018,7 +2104,7 @@ restore_entry(struct archive_write_disk *a) * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) - r = stat(a->name, &a->st); + r = la_stat(a->name, &a->st); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. @@ -2052,17 +2138,28 @@ restore_entry(struct archive_write_disk *a) } if (!S_ISDIR(a->st.st_mode)) { - /* A non-dir is in the way, unlink it. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); - if (unlink(a->name) != 0) { - archive_set_error(&a->archive, errno, - "Can't unlink already-existing object"); - return (ARCHIVE_FAILED); + + if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) && + S_ISREG(a->st.st_mode)) { + /* Use a temporary file to extract */ + if ((a->fd = la_mktemp(a)) == -1) + return ARCHIVE_FAILED; + a->pst = NULL; + en = 0; + } else { + /* A non-dir is in the way, unlink it. */ + if (unlink(a->name) != 0) { + archive_set_error(&a->archive, errno, + "Can't unlink already-existing " + "object"); + return (ARCHIVE_FAILED); + } + a->pst = NULL; + /* Try again. */ + en = create_filesystem_object(a); } - a->pst = NULL; - /* Try again. */ - en = create_filesystem_object(a); } else if (!S_ISDIR(a->mode)) { /* A dir is in the way of a non-dir, rmdir it. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) @@ -2164,6 +2261,13 @@ create_filesystem_object(struct archive_write_disk *a) } free(linkname_copy); archive_string_free(&error_string); + /* + * Unlinking and linking here is really not atomic, + * but doing it right, would require us to construct + * an mktemplink() function, and then use rename(2). + */ + if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) + unlink(a->name); r = link(linkname, a->name) ? errno : 0; /* * New cpio and pax formats allow hardlink entries @@ -2184,7 +2288,7 @@ create_filesystem_object(struct archive_write_disk *a) #ifdef HAVE_LSTAT r = lstat(a->name, &st); #else - r = stat(a->name, &st); + r = la_stat(a->name, &st); #endif if (r != 0) r = errno; @@ -2202,6 +2306,13 @@ create_filesystem_object(struct archive_write_disk *a) linkname = archive_entry_symlink(a->entry); if (linkname != NULL) { #if HAVE_SYMLINK + /* + * Unlinking and linking here is really not atomic, + * but doing it right, would require us to construct + * an mktempsymlink() function, and then use rename(2). + */ + if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) + unlink(a->name); return symlink(linkname, a->name) ? errno : 0; #else return (EPERM); @@ -2223,11 +2334,21 @@ create_filesystem_object(struct archive_write_disk *a) */ mode = final_mode & 0777 & ~a->user_umask; + /* + * Always create writable such that [f]setxattr() works if we're not + * root. + */ + if (a->user_uid != 0 && + a->todo & (TODO_HFS_COMPRESSION | TODO_XATTR)) { + mode |= 0200; + } + switch (a->mode & AE_IFMT) { default: /* POSIX requires that we fall through here. */ /* FALLTHROUGH */ case AE_IFREG: + a->tmpname = NULL; a->fd = open(a->name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode); __archive_ensure_cloexec_flag(a->fd); @@ -2319,7 +2440,7 @@ _archive_write_disk_close(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *next, *p; - int ret; + int fd, ret; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, @@ -2330,21 +2451,33 @@ _archive_write_disk_close(struct archive *_a) p = sort_dir_list(a->fixup_list); while (p != NULL) { + fd = -1; a->pst = NULL; /* Mark stat cache as out-of-date. */ + if (p->fixup & + (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) { + fd = open(p->name, + O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC); + } if (p->fixup & TODO_TIMES) { - set_times(a, -1, p->mode, p->name, + set_times(a, fd, p->mode, p->name, p->atime, p->atime_nanos, p->birthtime, p->birthtime_nanos, p->mtime, p->mtime_nanos, p->ctime, p->ctime_nanos); } - if (p->fixup & TODO_MODE_BASE) + if (p->fixup & TODO_MODE_BASE) { +#ifdef HAVE_FCHMOD + if (fd >= 0) + fchmod(fd, p->mode); + else +#endif chmod(p->name, p->mode); + } if (p->fixup & TODO_ACLS) - archive_write_disk_set_acls(&a->archive, -1, p->name, - &p->acl, p->mode); + archive_write_disk_set_acls(&a->archive, fd, + p->name, &p->acl, p->mode); if (p->fixup & TODO_FFLAGS) - set_fflags_platform(a, -1, p->name, + set_fflags_platform(a, fd, p->name, p->mode, p->fflags_set, 0); if (p->fixup & TODO_MAC_METADATA) set_mac_metadata(a, p->name, p->mac_metadata, @@ -2353,6 +2486,8 @@ _archive_write_disk_close(struct archive *_a) archive_acl_clear(&p->acl); free(p->mac_metadata); free(p->name); + if (fd >= 0) + close(fd); free(p); p = next; } @@ -2373,9 +2508,9 @@ _archive_write_disk_free(struct archive *_a) ret = _archive_write_disk_close(&a->archive); archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL); archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL); - if (a->entry) - archive_entry_free(a->entry); + archive_entry_free(a->entry); archive_string_free(&a->_name_data); + archive_string_free(&a->_tmpname_data); archive_string_free(&a->archive.error_string); archive_string_free(&a->path_safe); a->archive.magic = 0; @@ -2518,8 +2653,6 @@ fsobj_error(int *a_eno, struct archive_string *a_estr, * scan the path and both can be optimized by comparing against other * recent paths. */ -/* TODO: Extend this to support symlinks on Windows Vista and later. */ - /* * Checks the given path to see if any elements along it are symlinks. Returns * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. @@ -2528,7 +2661,8 @@ static int check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { -#if !defined(HAVE_LSTAT) +#if !defined(HAVE_LSTAT) && \ + !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)) /* Platform doesn't have lstat, so we can't look for symlinks. */ (void)path; /* UNUSED */ (void)error_number; /* UNUSED */ @@ -2543,7 +2677,10 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, char c; int r; struct stat st; - int restore_pwd; + int chdir_fd; +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + int fd; +#endif /* Nothing to do here if name is empty */ if(path[0] == '\0') @@ -2564,10 +2701,13 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, * c holds what used to be in *tail * last is 1 if this is the last tail */ - restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); - __archive_ensure_cloexec_flag(restore_pwd); - if (restore_pwd < 0) + chdir_fd = la_opendirat(AT_FDCWD, "."); + __archive_ensure_cloexec_flag(chdir_fd); + if (chdir_fd < 0) { + fsobj_error(a_eno, a_estr, errno, + "Could not open ", path); return (ARCHIVE_FATAL); + } head = path; tail = path; last = 0; @@ -2596,7 +2736,11 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, c = tail[0]; tail[0] = '\0'; /* Check that we haven't hit a symlink. */ +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW); +#else r = lstat(head, &st); +#endif if (r != 0) { tail[0] = c; /* We've hit a dir that doesn't exist; stop now. */ @@ -2622,7 +2766,19 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, } } else if (S_ISDIR(st.st_mode)) { if (!last) { - if (chdir(head) != 0) { +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + fd = la_opendirat(chdir_fd, head); + if (fd < 0) + r = -1; + else { + r = 0; + close(chdir_fd); + chdir_fd = fd; + } +#else + r = chdir(head); +#endif + if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not chdir ", path); @@ -2639,7 +2795,12 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, * so we can overwrite it with the * item being extracted. */ - if (unlink(head)) { +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + r = unlinkat(chdir_fd, head, 0); +#else + r = unlink(head); +#endif + if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not remove symlink ", @@ -2669,7 +2830,12 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, break; } else if (flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ - if (unlink(head) != 0) { +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + r = unlinkat(chdir_fd, head, 0); +#else + r = unlink(head); +#endif + if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot remove intervening " @@ -2687,7 +2853,11 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, * This is needed to extract hardlinks over * symlinks. */ - r = stat(head, &st); +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + r = fstatat(chdir_fd, head, &st, 0); +#else + r = la_stat(head, &st); +#endif if (r != 0) { tail[0] = c; if (errno == ENOENT) { @@ -2700,7 +2870,19 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, break; } } else if (S_ISDIR(st.st_mode)) { - if (chdir(head) != 0) { +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + fd = la_opendirat(chdir_fd, head); + if (fd < 0) + r = -1; + else { + r = 0; + close(chdir_fd); + chdir_fd = fd; + } +#else + r = chdir(head); +#endif + if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, @@ -2736,16 +2918,21 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, } /* Catches loop exits via break */ tail[0] = c; -#ifdef HAVE_FCHDIR +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + /* If we operate with openat(), fstatat() and unlinkat() there was + * no chdir(), so just close the fd */ + if (chdir_fd >= 0) + close(chdir_fd); +#elif HAVE_FCHDIR /* If we changed directory above, restore it here. */ - if (restore_pwd >= 0) { - r = fchdir(restore_pwd); + if (chdir_fd >= 0) { + r = fchdir(chdir_fd); if (r != 0) { fsobj_error(a_eno, a_estr, errno, "chdir() failure", ""); } - close(restore_pwd); - restore_pwd = -1; + close(chdir_fd); + chdir_fd = -1; if (r != 0) { res = (ARCHIVE_FATAL); } @@ -3027,7 +3214,7 @@ create_dir(struct archive_write_disk *a, char *path) * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ - if (stat(path, &st) == 0) { + if (la_stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { @@ -3085,7 +3272,7 @@ create_dir(struct archive_write_disk *a, char *path) * don't add it to the fixup list here, as it's already been * added. */ - if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) + if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%s'", @@ -3106,12 +3293,14 @@ create_dir(struct archive_write_disk *a, char *path) static int set_ownership(struct archive_write_disk *a) { -#ifndef __CYGWIN__ -/* unfortunately, on win32 there is no 'root' user with uid 0, - so we just have to try the chown and see if it works */ - - /* If we know we can't change it, don't bother trying. */ - if (a->user_uid != 0 && a->user_uid != a->uid) { +#if !defined(__CYGWIN__) && !defined(__linux__) +/* + * On Linux, a process may have the CAP_CHOWN capability. + * On Windows there is no 'root' user with uid 0. + * Elsewhere we can skip calling chown if we are not root and the desired + * user id does not match the current user. + */ + if (a->user_uid != 0 && a->user_uid != a->uid) { archive_set_error(&a->archive, errno, "Can't set UID=%jd", (intmax_t)a->uid); return (ARCHIVE_WARN); @@ -3360,6 +3549,7 @@ static int set_mode(struct archive_write_disk *a, int mode) { int r = ARCHIVE_OK; + int r2; mode &= 07777; /* Strip off file type bits. */ if (a->todo & TODO_SGID_CHECK) { @@ -3453,21 +3643,19 @@ set_mode(struct archive_write_disk *a, int mode) * post-extract fixup, which is handled elsewhere. */ #ifdef HAVE_FCHMOD - if (a->fd >= 0) { - if (fchmod(a->fd, mode) != 0) { - archive_set_error(&a->archive, errno, - "Can't set permissions to 0%o", (int)mode); - r = ARCHIVE_WARN; - } - } else + if (a->fd >= 0) + r2 = fchmod(a->fd, mode); + else #endif - /* If this platform lacks fchmod(), then - * we'll just use chmod(). */ - if (chmod(a->name, mode) != 0) { - archive_set_error(&a->archive, errno, - "Can't set permissions to 0%o", (int)mode); - r = ARCHIVE_WARN; - } + /* If this platform lacks fchmod(), then + * we'll just use chmod(). */ + r2 = chmod(a->name, mode); + + if (r2 != 0) { + archive_set_error(&a->archive, errno, + "Can't set permissions to 0%o", (int)mode); + r = ARCHIVE_WARN; + } } return (r); } @@ -3478,9 +3666,7 @@ set_fflags(struct archive_write_disk *a) struct fixup_entry *le; unsigned long set, clear; int r; - int critical_flags; mode_t mode = archive_entry_mode(a->entry); - /* * Make 'critical_flags' hold all file flags that can't be * immediately restored. For example, on BSD systems, @@ -3496,33 +3682,33 @@ set_fflags(struct archive_write_disk *a) * other programs that might try to muck with files as they're * being restored. */ - /* Hopefully, the compiler will optimize this mess into a constant. */ - critical_flags = 0; + const int critical_flags = 0 #ifdef SF_IMMUTABLE - critical_flags |= SF_IMMUTABLE; + | SF_IMMUTABLE #endif #ifdef UF_IMMUTABLE - critical_flags |= UF_IMMUTABLE; + | UF_IMMUTABLE #endif #ifdef SF_APPEND - critical_flags |= SF_APPEND; + | SF_APPEND #endif #ifdef UF_APPEND - critical_flags |= UF_APPEND; + | UF_APPEND #endif #if defined(FS_APPEND_FL) - critical_flags |= FS_APPEND_FL; + | FS_APPEND_FL #elif defined(EXT2_APPEND_FL) - critical_flags |= EXT2_APPEND_FL; + | EXT2_APPEND_FL #endif #if defined(FS_IMMUTABLE_FL) - critical_flags |= FS_IMMUTABLE_FL; + | FS_IMMUTABLE_FL #elif defined(EXT2_IMMUTABLE_FL) - critical_flags |= EXT2_IMMUTABLE_FL; + | EXT2_IMMUTABLE_FL #endif #ifdef FS_JOURNAL_DATA_FL - critical_flags |= FS_JOURNAL_DATA_FL; + | FS_JOURNAL_DATA_FL #endif + ; if (a->todo & TODO_FFLAGS) { archive_entry_fflags(a->entry, &set, &clear); @@ -3553,29 +3739,27 @@ set_fflags(struct archive_write_disk *a) static int clear_nochange_fflags(struct archive_write_disk *a) { - int nochange_flags; mode_t mode = archive_entry_mode(a->entry); - - /* Hopefully, the compiler will optimize this mess into a constant. */ - nochange_flags = 0; + const int nochange_flags = 0 #ifdef SF_IMMUTABLE - nochange_flags |= SF_IMMUTABLE; + | SF_IMMUTABLE #endif #ifdef UF_IMMUTABLE - nochange_flags |= UF_IMMUTABLE; + | UF_IMMUTABLE #endif #ifdef SF_APPEND - nochange_flags |= SF_APPEND; + | SF_APPEND #endif #ifdef UF_APPEND - nochange_flags |= UF_APPEND; + | UF_APPEND #endif #ifdef EXT2_APPEND_FL - nochange_flags |= EXT2_APPEND_FL; + | EXT2_APPEND_FL #endif #ifdef EXT2_IMMUTABLE_FL - nochange_flags |= EXT2_IMMUTABLE_FL; + | EXT2_IMMUTABLE_FL #endif + ; return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags)); @@ -3591,8 +3775,22 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int r; - + const int sf_mask = 0 +#ifdef SF_APPEND + | SF_APPEND +#endif +#ifdef SF_ARCHIVED + | SF_ARCHIVED +#endif +#ifdef SF_IMMUTABLE + | SF_IMMUTABLE +#endif +#ifdef SF_NOUNLINK + | SF_NOUNLINK +#endif + ; (void)mode; /* UNUSED */ + if (set == 0 && clear == 0) return (ARCHIVE_OK); @@ -3607,6 +3805,12 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, a->st.st_flags &= ~clear; a->st.st_flags |= set; + + /* Only super-user may change SF_* flags */ + + if (a->user_uid != 0) + a->st.st_flags &= ~sf_mask; + #ifdef HAVE_FCHFLAGS /* If platform has fchflags() and we were given an fd, use it. */ if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0) @@ -3648,22 +3852,6 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, int ret; int myfd = fd; int newflags, oldflags; - int sf_mask = 0; - - if (set == 0 && clear == 0) - return (ARCHIVE_OK); - /* Only regular files and dirs can have flags. */ - if (!S_ISREG(mode) && !S_ISDIR(mode)) - return (ARCHIVE_OK); - - /* If we weren't given an fd, open it ourselves. */ - if (myfd < 0) { - myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); - __archive_ensure_cloexec_flag(myfd); - } - if (myfd < 0) - return (ARCHIVE_OK); - /* * Linux has no define for the flags that are only settable by * the root user. This code may seem a little complex, but @@ -3671,19 +3859,36 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, * defines. (?) The code below degrades reasonably gracefully * if sf_mask is incomplete. */ + const int sf_mask = 0 #if defined(FS_IMMUTABLE_FL) - sf_mask |= FS_IMMUTABLE_FL; + | FS_IMMUTABLE_FL #elif defined(EXT2_IMMUTABLE_FL) - sf_mask |= EXT2_IMMUTABLE_FL; + | EXT2_IMMUTABLE_FL #endif #if defined(FS_APPEND_FL) - sf_mask |= FS_APPEND_FL; + | FS_APPEND_FL #elif defined(EXT2_APPEND_FL) - sf_mask |= EXT2_APPEND_FL; + | EXT2_APPEND_FL #endif #if defined(FS_JOURNAL_DATA_FL) - sf_mask |= FS_JOURNAL_DATA_FL; + | FS_JOURNAL_DATA_FL #endif + ; + + if (set == 0 && clear == 0) + return (ARCHIVE_OK); + /* Only regular files and dirs can have flags. */ + if (!S_ISREG(mode) && !S_ISDIR(mode)) + return (ARCHIVE_OK); + + /* If we weren't given an fd, open it ourselves. */ + if (myfd < 0) { + myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(myfd); + } + if (myfd < 0) + return (ARCHIVE_OK); + /* * XXX As above, this would be way simpler if we didn't have * to read the current flags from disk. XXX diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_private.h b/Utilities/cmlibarchive/libarchive/archive_write_disk_private.h index b655dea2b65..557d7e2bf34 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_private.h @@ -26,13 +26,13 @@ * $FreeBSD: head/lib/libarchive/archive_write_disk_private.h 201086 2009-12-28 02:17:53Z kientzle $ */ +#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED +#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED -#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED - #include "archive_platform_acl.h" #include "archive_acl_private.h" #include "archive_entry.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c index 5c766d75dd0..5fccdb9dc65 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c @@ -114,8 +114,7 @@ lookup_gid(void *private_data, const char *gname, int64_t gid) return ((gid_t)b->id); /* Free the cache slot for a new entry. */ - if (b->name != NULL) - free(b->name); + free(b->name); b->name = strdup(gname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; @@ -184,8 +183,7 @@ lookup_uid(void *private_data, const char *uname, int64_t uid) return ((uid_t)b->id); /* Free the cache slot for a new entry. */ - if (b->name != NULL) - free(b->name); + free(b->name); b->name = strdup(uname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c index 78eda4abc8d..77e36c4a621 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c @@ -165,6 +165,8 @@ struct archive_write_disk { struct archive_entry *entry; /* Entry being extracted. */ wchar_t *name; /* Name of entry, possibly edited. */ struct archive_wstring _name_data; /* backing store for 'name' */ + wchar_t *tmpname; /* Temporary name */ + struct archive_wstring _tmpname_data; /* backing store for 'tmpname' */ /* Tasks remaining for this object. */ int todo; /* Tasks deferred until end-of-archive. */ @@ -205,6 +207,8 @@ struct archive_write_disk { #define MINIMUM_DIR_MODE 0700 #define MAXIMUM_DIR_MODE 0775 +static int disk_unlink(const wchar_t *); +static int disk_rmdir(const wchar_t *); static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, @@ -213,13 +217,17 @@ static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, wchar_t *); static int create_parent_dir(struct archive_write_disk *, wchar_t *); static int la_chmod(const wchar_t *, mode_t); +static int la_mktemp(struct archive_write_disk *); static int older(BY_HANDLE_FILE_INFORMATION *, struct archive_entry *); static int permissive_name_w(struct archive_write_disk *); static int restore_entry(struct archive_write_disk *); static int set_acls(struct archive_write_disk *, HANDLE h, const wchar_t *, struct archive_acl *); static int set_xattrs(struct archive_write_disk *); +static int clear_nochange_fflags(struct archive_write_disk *); static int set_fflags(struct archive_write_disk *); +static int set_fflags_platform(const wchar_t *, unsigned long, + unsigned long); static int set_ownership(struct archive_write_disk *); static int set_mode(struct archive_write_disk *, int mode); static int set_times(struct archive_write_disk *, HANDLE, int, @@ -474,6 +482,11 @@ permissive_name_w(struct archive_write_disk *a) { archive_wstrncpy(&(a->_name_data), wsp, l); } + else if (l > 2 && wsp[0] == L'\\' && wsp[1] == L'\\' && wsp[2] != L'\\') + { + archive_wstrncpy(&(a->_name_data), L"\\\\?\\UNC\\", 8); + archive_wstrncat(&(a->_name_data), wsp+2, l-2); + } else { archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4); @@ -524,6 +537,28 @@ la_chmod(const wchar_t *path, mode_t mode) return (ret); } +static int +la_mktemp(struct archive_write_disk *a) +{ + int fd; + mode_t mode; + + archive_wstring_empty(&(a->_tmpname_data)); + archive_wstrcpy(&(a->_tmpname_data), a->name); + archive_wstrcat(&(a->_tmpname_data), L".XXXXXX"); + a->tmpname = a->_tmpname_data.s; + + fd = __archive_mkstemp(a->tmpname); + + mode = a->mode & 0777 & ~a->user_umask; + if (la_chmod(a->tmpname, mode) == -1) { + la_dosmaperr(GetLastError()); + _close(fd); + return -1; + } + return (fd); +} + static void * la_GetFunctionKernel32(const char *name) { @@ -551,8 +586,10 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) set = 1; f = la_GetFunctionKernel32("CreateHardLinkW"); } - if (!f) + if (!f) { + errno = ENOTSUP; return (0); + } ret = (*f)(linkname, target, NULL); if (!ret) { /* Under windows 2000, it is necessary to remove @@ -577,6 +614,103 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) return (ret); } +/* + * Create file or directory symolic link + * + * If linktype is AE_SYMLINK_TYPE_UNDEFINED (or unknown), guess linktype from + * the link target + */ +static int +la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, + int linktype) { + static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD); + static int set; + wchar_t *ttarget, *p; + int len; + DWORD attrs = 0; + DWORD flags = 0; + DWORD newflags = 0; + BOOL ret = 0; + + if (!set) { + set = 1; + f = la_GetFunctionKernel32("CreateSymbolicLinkW"); + } + if (!f) + return (0); + + len = wcslen(target); + if (len == 0) { + errno = EINVAL; + return(0); + } + /* + * When writing path targets, we need to translate slashes + * to backslashes + */ + ttarget = malloc((len + 1) * sizeof(wchar_t)); + if (ttarget == NULL) + return(0); + + p = ttarget; + + while(*target != L'\0') { + if (*target == L'/') + *p = L'\\'; + else + *p = *target; + target++; + p++; + } + *p = L'\0'; + + /* + * In case of undefined symlink type we guess it from the target. + * If the target equals ".", "..", ends with a backslash or a + * backslash followed by "." or ".." we assume it is a directory + * symlink. In all other cases we assume a file symlink. + */ + if (linktype != AE_SYMLINK_TYPE_FILE && ( + linktype == AE_SYMLINK_TYPE_DIRECTORY || + *(p - 1) == L'\\' || (*(p - 1) == L'.' && ( + len == 1 || *(p - 2) == L'\\' || ( *(p - 2) == L'.' && ( + len == 2 || *(p - 3) == L'\\')))))) { +#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY) + flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; +#else + flags |= 0x1; +#endif + } + +#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) + newflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; +#else + newflags = flags | 0x2; +#endif + + /* + * Windows won't overwrite existing links + */ + attrs = GetFileAttributesW(linkname); + if (attrs != INVALID_FILE_ATTRIBUTES) { + if (attrs & FILE_ATTRIBUTE_DIRECTORY) + disk_rmdir(linkname); + else + disk_unlink(linkname); + } + + ret = (*f)(linkname, ttarget, newflags); + /* + * Prior to Windows 10 calling CreateSymbolicLinkW() will fail + * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set + */ + if (!ret) { + ret = (*f)(linkname, ttarget, flags); + } + free(ttarget); + return (ret); +} + static int la_ftruncate(HANDLE handle, int64_t length) { @@ -696,10 +830,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) a->pst = NULL; a->current_fixup = NULL; a->deferred = 0; - if (a->entry) { - archive_entry_free(a->entry); - a->entry = NULL; - } + archive_entry_free(a->entry); + a->entry = NULL; a->entry = archive_entry_clone(entry); a->fh = INVALID_HANDLE_VALUE; a->fd_offset = 0; @@ -860,9 +992,11 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) } if (a->deferred & TODO_FFLAGS) { + unsigned long set, clear; + fe = current_fixup(a, archive_entry_pathname_w(entry)); - fe->fixup |= TODO_FFLAGS; - /* TODO: Complete this.. defer fflags from below. */ + archive_entry_fflags(entry, &set, &clear); + fe->fflags_set = set; } /* @@ -1143,12 +1277,20 @@ _archive_write_disk_finish_entry(struct archive *_a) if (a->fh != INVALID_HANDLE_VALUE) { CloseHandle(a->fh); a->fh = INVALID_HANDLE_VALUE; + if (a->tmpname) { + /* Windows does not support atomic rename */ + disk_unlink(a->name); + if (_wrename(a->tmpname, a->name) != 0) { + archive_set_error(&a->archive, errno, + "rename failed"); + ret = ARCHIVE_FATAL; + } + a->tmpname = NULL; + } } /* If there's an entry, we can release it now. */ - if (a->entry) { - archive_entry_free(a->entry); - a->entry = NULL; - } + archive_entry_free(a->entry); + a->entry = NULL; a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } @@ -1239,7 +1381,7 @@ archive_write_disk_new(void) } static int -disk_unlink(wchar_t *path) +disk_unlink(const wchar_t *path) { wchar_t *fullname; int r; @@ -1254,7 +1396,7 @@ disk_unlink(wchar_t *path) } static int -disk_rmdir(wchar_t *path) +disk_rmdir(const wchar_t *path) { wchar_t *fullname; int r; @@ -1285,6 +1427,8 @@ restore_entry(struct archive_write_disk *a) * object is a dir, but that doesn't mean the old * object isn't a dir. */ + if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) + (void)clear_nochange_fflags(a); if (disk_unlink(a->name) == 0) { /* We removed it, reset cached stat. */ a->pst = NULL; @@ -1359,28 +1503,45 @@ restore_entry(struct archive_write_disk *a) en = create_filesystem_object(a); } else if (en == EEXIST) { mode_t st_mode; + mode_t lst_mode; + BY_HANDLE_FILE_INFORMATION lst; /* * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ int r = 0; + int dirlnk = 0; + /* * The SECURE_SYMLINK logic has already removed a * symlink to a dir if the client wants that. So * follow the symlink if we're creating a dir. - */ - if (S_ISDIR(a->mode)) - r = file_information(a, a->name, &a->st, &st_mode, 0); - /* * If it's not a dir (or it's a broken symlink), * then don't follow it. + * + * Windows distinguishes file and directory symlinks. + * A file symlink may erroneously point to a directory + * and a directory symlink to a file. Windows does not follow + * such symlinks. We always need both source and target + * information. */ - if (r != 0 || !S_ISDIR(a->mode)) - r = file_information(a, a->name, &a->st, &st_mode, 1); + r = file_information(a, a->name, &lst, &lst_mode, 1); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); return (ARCHIVE_FAILED); + } else if (S_ISLNK(lst_mode)) { + if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + dirlnk = 1; + /* In case of a symlink we need target information */ + r = file_information(a, a->name, &a->st, &st_mode, 0); + if (r != 0) { + a->st = lst; + st_mode = lst_mode; + } + } else { + a->st = lst; + st_mode = lst_mode; } /* @@ -1404,17 +1565,50 @@ restore_entry(struct archive_write_disk *a) } if (!S_ISDIR(st_mode)) { - /* A non-dir is in the way, unlink it. */ - if (disk_unlink(a->name) != 0) { - archive_set_error(&a->archive, errno, - "Can't unlink already-existing object"); - return (ARCHIVE_FAILED); + if (a->flags & + ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) { + (void)clear_nochange_fflags(a); + } + if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) && + S_ISREG(st_mode)) { + int fd = la_mktemp(a); + + if (fd == -1) + return (ARCHIVE_FAILED); + a->fh = (HANDLE)_get_osfhandle(fd); + if (a->fh == INVALID_HANDLE_VALUE) + return (ARCHIVE_FAILED); + + a->pst = NULL; + en = 0; + } else { + if (dirlnk) { + /* Edge case: dir symlink pointing + * to a file */ + if (disk_rmdir(a->name) != 0) { + archive_set_error(&a->archive, + errno, "Can't unlink " + "directory symlink"); + return (ARCHIVE_FAILED); + } + } else { + if (disk_unlink(a->name) != 0) { + /* A non-dir is in the way, + * unlink it. */ + archive_set_error(&a->archive, + errno, "Can't unlink " + "already-existing object"); + return (ARCHIVE_FAILED); + } + } + a->pst = NULL; + /* Try again. */ + en = create_filesystem_object(a); } - a->pst = NULL; - /* Try again. */ - en = create_filesystem_object(a); } else if (!S_ISDIR(a->mode)) { /* A dir is in the way of a non-dir, rmdir it. */ + if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) + (void)clear_nochange_fflags(a); if (disk_rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); @@ -1462,6 +1656,7 @@ create_filesystem_object(struct archive_write_disk *a) wchar_t *fullname; mode_t final_mode, mode; int r; + DWORD attrs = 0; /* We identify hard/symlinks according to the link names. */ /* Since link(2) and symlink(2) don't handle modes, we're done here. */ @@ -1475,6 +1670,20 @@ create_filesystem_object(struct archive_write_disk *a) errno = EINVAL; r = -1; } else { + /* + * Unlinking and linking here is really not atomic, + * but doing it right, would require us to construct + * an mktemplink() function, and then use _wrename(). + */ + if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) { + attrs = GetFileAttributesW(namefull); + if (attrs != INVALID_FILE_ATTRIBUTES) { + if (attrs & FILE_ATTRIBUTE_DIRECTORY) + disk_rmdir(namefull); + else + disk_unlink(namefull); + } + } r = la_CreateHardLinkW(namefull, linkfull); if (r == 0) { la_dosmaperr(GetLastError()); @@ -1511,10 +1720,31 @@ create_filesystem_object(struct archive_write_disk *a) } linkname = archive_entry_symlink_w(a->entry); if (linkname != NULL) { + /* + * Unlinking and linking here is really not atomic, + * but doing it right, would require us to construct + * an mktemplink() function, and then use _wrename(). + */ + attrs = GetFileAttributesW(a->name); + if (attrs != INVALID_FILE_ATTRIBUTES) { + if (attrs & FILE_ATTRIBUTE_DIRECTORY) + disk_rmdir(a->name); + else + disk_unlink(a->name); + } #if HAVE_SYMLINK return symlink(linkname, a->name) ? errno : 0; #else - return (EPERM); + errno = 0; + r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname, + archive_entry_symlink_type(a->entry)); + if (r == 0) { + if (errno == 0) + la_dosmaperr(GetLastError()); + r = errno; + } else + r = 0; + return (r); #endif } @@ -1538,6 +1768,7 @@ create_filesystem_object(struct archive_write_disk *a) /* POSIX requires that we fall through here. */ /* FALLTHROUGH */ case AE_IFREG: + a->tmpname = NULL; fullname = a->name; /* O_WRONLY | O_CREAT | O_EXCL */ a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL, @@ -1667,6 +1898,8 @@ _archive_write_disk_close(struct archive *_a) la_chmod(p->name, p->mode); if (p->fixup & TODO_ACLS) set_acls(a, INVALID_HANDLE_VALUE, p->name, &p->acl); + if (p->fixup & TODO_FFLAGS) + set_fflags_platform(p->name, p->fflags_set, 0); next = p->next; archive_acl_clear(&p->acl); free(p->name); @@ -1690,9 +1923,9 @@ _archive_write_disk_free(struct archive *_a) ret = _archive_write_disk_close(&a->archive); archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL); archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL); - if (a->entry) - archive_entry_free(a->entry); + archive_entry_free(a->entry); archive_wstring_free(&a->_name_data); + archive_wstring_free(&a->_tmpname_data); archive_string_free(&a->archive.error_string); archive_wstring_free(&a->path_safe); a->archive.magic = 0; @@ -1784,6 +2017,7 @@ new_fixup(struct archive_write_disk *a, const wchar_t *pathname) a->fixup_list = fe; fe->fixup = 0; fe->name = _wcsdup(pathname); + fe->fflags_set = 0; return (fe); } @@ -1798,7 +2032,6 @@ current_fixup(struct archive_write_disk *a, const wchar_t *pathname) return (a->current_fixup); } -/* TODO: Make this work. */ /* * TODO: The deep-directory support bypasses this; disable deep directory * support if we're doing symlink checks. @@ -1808,7 +2041,6 @@ current_fixup(struct archive_write_disk *a, const wchar_t *pathname) * scan the path and both can be optimized by comparing against other * recent paths. */ -/* TODO: Extend this to support symlinks on Windows Vista and later. */ static int check_symlinks(struct archive_write_disk *a) { @@ -1827,6 +2059,9 @@ check_symlinks(struct archive_write_disk *a) p = a->path_safe.s; while ((*pn != '\0') && (*p == *pn)) ++p, ++pn; + /* Skip leading backslashes */ + while (*pn == '\\') + ++pn; c = pn[0]; /* Keep going until we've checked the entire name. */ while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) { @@ -1844,11 +2079,21 @@ check_symlinks(struct archive_write_disk *a) } else if (S_ISLNK(st_mode)) { if (c == '\0') { /* - * Last element is symlink; remove it - * so we can overwrite it with the + * Last element is a file or directory symlink. + * Remove it so we can overwrite it with the * item being extracted. */ - if (disk_unlink(a->name)) { + if (a->flags & + ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) { + (void)clear_nochange_fflags(a); + } + if (st.dwFileAttributes & + FILE_ATTRIBUTE_DIRECTORY) { + r = disk_rmdir(a->name); + } else { + r = disk_unlink(a->name); + } + if (r) { archive_set_error(&a->archive, errno, "Could not remove symlink %ls", a->name); @@ -1872,7 +2117,17 @@ check_symlinks(struct archive_write_disk *a) return (0); } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ - if (disk_unlink(a->name) != 0) { + if (a->flags & + ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) { + (void)clear_nochange_fflags(a); + } + if (st.dwFileAttributes & + FILE_ATTRIBUTE_DIRECTORY) { + r = disk_rmdir(a->name); + } else { + r = disk_unlink(a->name); + } + if (r != 0) { archive_set_error(&a->archive, 0, "Cannot remove intervening " "symlink %ls", a->name); @@ -1888,6 +2143,8 @@ check_symlinks(struct archive_write_disk *a) return (ARCHIVE_FAILED); } } + pn[0] = c; + pn++; } pn[0] = c; /* We've checked and/or cleaned the whole path, so remember it. */ @@ -2438,10 +2695,56 @@ set_mode(struct archive_write_disk *a, int mode) return (r); } +static int set_fflags_platform(const wchar_t *name, unsigned long fflags_set, + unsigned long fflags_clear) +{ + DWORD oldflags, newflags; + wchar_t *fullname; + + const DWORD settable_flags = + FILE_ATTRIBUTE_ARCHIVE | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_NORMAL | + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | + FILE_ATTRIBUTE_OFFLINE | + FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_TEMPORARY; + + oldflags = GetFileAttributesW(name); + if (oldflags == (DWORD)-1 && + GetLastError() == ERROR_INVALID_NAME) { + fullname = __la_win_permissive_name_w(name); + oldflags = GetFileAttributesW(fullname); + } + if (oldflags == (DWORD)-1) { + la_dosmaperr(GetLastError()); + return (ARCHIVE_WARN); + } + newflags = ((oldflags & ~fflags_clear) | fflags_set) & settable_flags; + if(SetFileAttributesW(name, newflags) == 0) + return (ARCHIVE_WARN); + return (ARCHIVE_OK); +} + +static int +clear_nochange_fflags(struct archive_write_disk *a) +{ + return (set_fflags_platform(a->name, 0, FILE_ATTRIBUTE_READONLY)); +} + static int set_fflags(struct archive_write_disk *a) { - (void)a; /* UNUSED */ + unsigned long set, clear; + + if (a->todo & TODO_FFLAGS) { + archive_entry_fflags(a->entry, &set, &clear); + if (set == 0 && clear == 0) + return (ARCHIVE_OK); + return (set_fflags_platform(a->name, set, clear)); + + } return (ARCHIVE_OK); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_filter.3 b/Utilities/cmlibarchive/libarchive/archive_write_filter.3 index d6fa07131a8..c83eb77b6a5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_filter.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_filter.3 @@ -43,7 +43,7 @@ .Nm archive_write_add_filter_program , .Nm archive_write_add_filter_uuencode , .Nm archive_write_add_filter_xz , -.Nm archive_write_add_filter_zstd , +.Nm archive_write_add_filter_zstd .Nd functions enabling output filters .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) @@ -125,10 +125,10 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write 3 , .Xr archive_write_format 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_write_finish_entry.3 b/Utilities/cmlibarchive/libarchive/archive_write_finish_entry.3 index dc1b94b82a5..5797e16a6db 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_finish_entry.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_finish_entry.3 @@ -71,9 +71,9 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write_data 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_write_format.3 b/Utilities/cmlibarchive/libarchive/archive_write_format.3 index aaafb0a8617..47a74033962 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_format.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_format.3 @@ -52,7 +52,7 @@ .Nm archive_write_set_format_v7tar , .Nm archive_write_set_format_warc , .Nm archive_write_set_format_xar , -.Nm archive_write_set_format_zip , +.Nm archive_write_set_format_zip .Nd functions for creating archives .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) @@ -166,9 +166,9 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr libarchive-formats 5 , .Xr mtree 5 , diff --git a/Utilities/cmlibarchive/libarchive/archive_write_free.3 b/Utilities/cmlibarchive/libarchive/archive_write_free.3 index 1b2d07131d8..5210e2a633d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_free.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_free.3 @@ -56,7 +56,7 @@ after calling this function, the only call that can succeed is to release the resources. This can be used to speed recovery when the archive creation must be aborted. -Note that the created archive is likely to be malformed in this case; +Note that the created archive is likely to be malformed in this case; .It Fn archive_write_close Complete the archive and invoke the close callback. .It Fn archive_write_finish @@ -89,8 +89,8 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_write_header.3 b/Utilities/cmlibarchive/libarchive/archive_write_header.3 index 4de58f3b9f3..2217b1871bb 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_header.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_header.3 @@ -66,8 +66,8 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_write_new.3 b/Utilities/cmlibarchive/libarchive/archive_write_new.3 index f05d269d3e8..788cbb85598 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_new.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_new.3 @@ -50,9 +50,9 @@ object can be found in the overview manual page for .\" .Sh ERRORS .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open.3 b/Utilities/cmlibarchive/libarchive/archive_write_open.3 index 457873e6148..0129d10b7f2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_open.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_open.3 @@ -200,7 +200,7 @@ On failure, the callback should invoke .Fn archive_set_error to register an error code and message and return -.Cm ARCHIVE_FATAL. +.Cm ARCHIVE_FATAL . .Pp Note that if the client-provided write callback function returns a non-zero value, that error will be propagated back to the caller @@ -234,13 +234,13 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write 3 , .Xr archive_write_blocksize 3 , .Xr archive_write_filter 3 , .Xr archive_write_format 3 , .Xr archive_write_new 3 , .Xr archive_write_set_options 3 , +.Xr libarchive 3 , .Xr cpio 5 , .Xr mtree 5 , .Xr tar 5 diff --git a/Utilities/cmlibarchive/libarchive/archive_write_private.h b/Utilities/cmlibarchive/libarchive/archive_write_private.h index 0dfd1b1bca9..27cba0392ce 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_write_private.h @@ -25,19 +25,24 @@ * $FreeBSD: head/lib/libarchive/archive_write_private.h 201155 2009-12-29 05:20:12Z kientzle $ */ +#ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED +#define ARCHIVE_WRITE_PRIVATE_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif #endif -#ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED -#define ARCHIVE_WRITE_PRIVATE_H_INCLUDED - #include "archive.h" #include "archive_string.h" #include "archive_private.h" +#define ARCHIVE_WRITE_FILTER_STATE_NEW 1U +#define ARCHIVE_WRITE_FILTER_STATE_OPEN 2U +#define ARCHIVE_WRITE_FILTER_STATE_CLOSED 4U +#define ARCHIVE_WRITE_FILTER_STATE_FATAL 0x8000U + struct archive_write; struct archive_write_filter { @@ -55,6 +60,7 @@ struct archive_write_filter { int code; int bytes_per_block; int bytes_in_last_block; + int state; }; #if ARCHIVE_VERSION < 4000000 @@ -66,8 +72,6 @@ struct archive_write_filter *__archive_write_allocate_filter(struct archive *); int __archive_write_output(struct archive_write *, const void *, size_t); int __archive_write_nulls(struct archive_write *, size_t); int __archive_write_filter(struct archive_write_filter *, const void *, size_t); -int __archive_write_open_filter(struct archive_write_filter *); -int __archive_write_close_filter(struct archive_write_filter *); struct archive_write { struct archive archive; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format.c index 0f706231add..12de0807753 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format.c 201168 2009-1 #include "archive.h" #include "archive_private.h" +#include "archive_write_set_format_private.h" /* A table that maps format codes to functions. */ static const @@ -76,3 +77,47 @@ archive_write_set_format(struct archive *a, int code) archive_set_error(a, EINVAL, "No such format"); return (ARCHIVE_FATAL); } + +void +__archive_write_entry_filetype_unsupported(struct archive *a, + struct archive_entry *entry, const char *format) +{ + char *name = NULL; + + switch (archive_entry_filetype(entry)) { + /* + * All formats should be able to archive regular files (AE_IFREG) + */ + case AE_IFDIR: + name = "directories"; + break; + case AE_IFLNK: + name = "symbolic links"; + break; + case AE_IFCHR: + name = "character devices"; + break; + case AE_IFBLK: + name = "block devices"; + break; + case AE_IFIFO: + name = "named pipes"; + break; + case AE_IFSOCK: + name = "sockets"; + break; + default: + break; + } + + if (name != NULL) { + archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, + "%s: %s format cannot archive %s", + archive_entry_pathname(entry), format, name); + } else { + archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, + "%s: %s format cannot archive files with mode 0%lo", + archive_entry_pathname(entry), format, + (unsigned long)archive_entry_mode(entry)); + } +} diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c index 2bd4ec41fef..69af814f78a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c @@ -31,13 +31,13 @@ __FBSDID("$FreeBSD$"); #endif #include #ifdef HAVE_BZLIB_H -#include +#include #endif #if HAVE_LZMA_H -#include +#include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include "archive_rb.h" #include "archive_string.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" /* * Codec ID @@ -164,7 +165,7 @@ struct file { mode_t mode; uint32_t crc32; - int dir:1; + signed int dir:1; }; struct _7zip { @@ -439,7 +440,8 @@ _7z_write_header(struct archive_write *a, struct archive_entry *entry) r = file_new(a, entry, &file); if (r < ARCHIVE_WARN) { - file_free(file); + if (file != NULL) + file_free(file); return (r); } if (file->size == 0 && file->dir) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c index 50305ccbeda..fc0de1e9f6f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ar.c 201108 200 #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" struct ar_w { uint64_t entry_bytes_remaining; @@ -187,6 +188,11 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) buff[AR_name_offset] = '/'; goto stat; } + if (strcmp(pathname, "/SYM64/") == 0) { + /* Entry is archive symbol table in GNU 64-bit format */ + memcpy(buff + AR_name_offset, "/SYM64/", 7); + goto stat; + } if (strcmp(pathname, "__.SYMDEF") == 0) { /* Entry is archive symbol table in BSD format */ memcpy(buff + AR_name_offset, "__.SYMDEF", 9); diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c index a4c9d1ed276..729f9c77559 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2 #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" static ssize_t archive_write_cpio_data(struct archive_write *, const void *buff, size_t s); @@ -408,8 +409,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) } } exit_write_header: - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret_final); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c index 957f1a333a6..172fda62f0b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio_newc.c 201 #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" static ssize_t archive_write_newc_data(struct archive_write *, const void *buff, size_t s); @@ -366,8 +367,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) } } exit_write_header: - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret_final); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c index 2d858c9f752..ec29c5c418e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_gnu_tar.c 19157 #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" struct gnutar { uint64_t entry_bytes_remaining; @@ -339,7 +340,7 @@ archive_write_gnutar_header(struct archive_write *a, * case getting WCS failed. On POSIX, this is a * normal operation. */ - if (p != NULL && p[strlen(p) - 1] != '/') { + if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); @@ -534,17 +535,9 @@ archive_write_gnutar_header(struct archive_write *a, case AE_IFBLK: tartype = '4' ; break; case AE_IFDIR: tartype = '5' ; break; case AE_IFIFO: tartype = '6' ; break; - case AE_IFSOCK: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive socket"); - ret = ARCHIVE_FAILED; - goto exit_write_header; - default: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive this (mode=0%lo)", - (unsigned long)archive_entry_mode(entry)); + default: /* AE_IFSOCK and unknown */ + __archive_write_entry_filetype_unsupported( + &a->archive, entry, "gnutar"); ret = ARCHIVE_FAILED; goto exit_write_header; } @@ -565,8 +558,7 @@ archive_write_gnutar_header(struct archive_write *a, gnutar->entry_bytes_remaining = archive_entry_size(entry); gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining); exit_write_header: - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c index 4adf68e682c..5db414f401a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c @@ -47,7 +47,7 @@ #include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" @@ -289,12 +289,12 @@ struct isoent { struct extr_rec *current; } extr_rec_list; - int virtual:1; + signed int virtual:1; /* If set to one, this file type is a directory. * A convenience flag to be used as * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR". */ - int dir:1; + signed int dir:1; }; struct hardlink { @@ -755,9 +755,9 @@ struct iso9660 { /* Used for making zisofs. */ struct { - int detect_magic:1; - int making:1; - int allzero:1; + signed int detect_magic:1; + signed int making:1; + signed int allzero:1; unsigned char magic_buffer[64]; int magic_cnt; @@ -3650,7 +3650,7 @@ wb_consume(struct archive_write *a, size_t size) if (size > iso9660->wbuff_remaining || iso9660->wbuff_remaining == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal Programing error: iso9660:wb_consume()" + "Internal Programming error: iso9660:wb_consume()" " size=%jd, wbuff_remaining=%jd", (intmax_t)size, (intmax_t)iso9660->wbuff_remaining); return (ARCHIVE_FATAL); @@ -3671,7 +3671,7 @@ wb_set_offset(struct archive_write *a, int64_t off) if (iso9660->wbuff_type != WB_TO_TEMP) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal Programing error: iso9660:wb_set_offset()"); + "Internal Programming error: iso9660:wb_set_offset()"); return (ARCHIVE_FATAL); } @@ -4899,10 +4899,10 @@ isofile_gen_utility_names(struct archive_write *a, struct isofile *file) if (p[0] == '/') { if (p[1] == '/') /* Convert '//' --> '/' */ - strcpy(p, p+1); + memmove(p, p+1, strlen(p+1) + 1); else if (p[1] == '.' && p[2] == '/') /* Convert '/./' --> '/' */ - strcpy(p, p+2); + memmove(p, p+2, strlen(p+2) + 1); else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { /* Convert 'dir/dir1/../dir2/' * --> 'dir/dir2/' @@ -5094,13 +5094,11 @@ isofile_init_hardlinks(struct iso9660 *iso9660) static void isofile_free_hardlinks(struct iso9660 *iso9660) { - struct archive_rb_node *n, *next; + struct archive_rb_node *n, *tmp; - for (n = ARCHIVE_RB_TREE_MIN(&(iso9660->hardlink_rbtree)); n;) { - next = __archive_rb_tree_iterate(&(iso9660->hardlink_rbtree), - n, ARCHIVE_RB_DIR_RIGHT); + ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(iso9660->hardlink_rbtree), tmp) { + __archive_rb_tree_remove_node(&(iso9660->hardlink_rbtree), n); free(n); - n = next; } } @@ -7801,8 +7799,8 @@ struct zisofs_extract { uint64_t pz_uncompressed_size; size_t uncompressed_buffer_size; - int initialized:1; - int header_passed:1; + signed int initialized:1; + signed int header_passed:1; uint32_t pz_offset; unsigned char *block_pointers; @@ -8128,7 +8126,7 @@ zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) { (void)buff; /* UNUSED */ (void)s; /* UNUSED */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programing error"); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programming error"); return (ARCHIVE_FATAL); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c index 493d4735661..aa41e9acc22 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c @@ -186,7 +186,7 @@ struct mtree_writer { #endif /* Keyword options */ int keys; -#define F_CKSUM 0x00000001 /* check sum */ +#define F_CKSUM 0x00000001 /* checksum */ #define F_DEV 0x00000002 /* device type */ #define F_DONE 0x00000004 /* directory done */ #define F_FLAGS 0x00000008 /* file flags */ @@ -371,7 +371,7 @@ mtree_quote(struct archive_string *s, const char *str) } /* - * Indent a line as mtree utility to be readable for people. + * Indent a line as the mtree utility does so it is readable for people. */ static void mtree_indent(struct mtree_writer *mtree) @@ -446,8 +446,8 @@ mtree_indent(struct mtree_writer *mtree) /* * Write /set keyword. - * Set most used value of uid,gid,mode and fflags, which are - * collected by attr_counter_set_collect() function. + * Set the most used value of uid, gid, mode and fflags, which are + * collected by the attr_counter_set_collect() function. */ static void write_global(struct mtree_writer *mtree) @@ -649,7 +649,7 @@ attr_counter_inc(struct attr_counter **top, struct attr_counter *ac, } /* - * Tabulate uid,gid,mode and fflags of a entry in order to be used for /set. + * Tabulate uid, gid, mode and fflags of a entry in order to be used for /set. */ static int attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me) @@ -912,7 +912,7 @@ archive_write_mtree_header(struct archive_write *a, /* If the current file is a regular file, we have to * compute the sum of its content. - * Initialize a bunch of sum check context. */ + * Initialize a bunch of checksum context. */ if (mtree_entry->reg_info) sum_init(mtree); @@ -1265,7 +1265,7 @@ archive_write_mtree_free(struct archive_write *a) if (mtree == NULL) return (ARCHIVE_OK); - /* Make sure we dot not leave any entries. */ + /* Make sure we do not leave any entries. */ mtree_entry_register_free(mtree); archive_string_free(&mtree->cur_dirstr); archive_string_free(&mtree->ebuf); @@ -1810,10 +1810,10 @@ mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file, if (p[0] == '/') { if (p[1] == '/') /* Convert '//' --> '/' */ - strcpy(p, p+1); + memmove(p, p+1, strlen(p+1) + 1); else if (p[1] == '.' && p[2] == '/') /* Convert '/./' --> '/' */ - strcpy(p, p+2); + memmove(p, p+2, strlen(p+2) + 1); else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { /* Convert 'dir/dir1/../dir2/' * --> 'dir/dir2/' @@ -2024,7 +2024,7 @@ mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep) if (file->parentdir.length == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal programing error " + "Internal programming error " "in generating canonical name for %s", file->pathname.s); return (ARCHIVE_FAILED); diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c index 3cebeae187d..a2b27107195 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_pax.c 201162 20 #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" struct sparse_block { struct sparse_block *next; @@ -199,6 +200,28 @@ archive_write_pax_options(struct archive_write *a, const char *key, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "pax: invalid charset name"); return (ret); + } else if (strcmp(key, "xattrheader") == 0) { + if (val == NULL || val[0] == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "pax: xattrheader requires a value"); + } else if (strcmp(val, "ALL") == 0 || + strcmp(val, "all") == 0) { + pax->flags |= WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR; + ret = ARCHIVE_OK; + } else if (strcmp(val, "SCHILY") == 0 || + strcmp(val, "schily") == 0) { + pax->flags |= WRITE_SCHILY_XATTR; + pax->flags &= ~WRITE_LIBARCHIVE_XATTR; + ret = ARCHIVE_OK; + } else if (strcmp(val, "LIBARCHIVE") == 0 || + strcmp(val, "libarchive") == 0) { + pax->flags |= WRITE_LIBARCHIVE_XATTR; + pax->flags &= ~WRITE_SCHILY_XATTR; + ret = ARCHIVE_OK; + } else + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "pax: invalid xattr header name"); + return (ret); } /* Note: The "warn" return is just to inform the options @@ -522,11 +545,13 @@ add_pax_acl(struct archive_write *a, ARCHIVE_ERRNO_FILE_FORMAT, "%s %s %s", "Can't translate ", attr, " to UTF-8"); return(ARCHIVE_WARN); - } else if (*p != '\0') { + } + + if (*p != '\0') { add_pax_attr(&(pax->pax_header), attr, p); - free(p); } + free(p); return(ARCHIVE_OK); } @@ -660,7 +685,7 @@ archive_write_pax_header(struct archive_write *a, * case getting WCS failed. On POSIX, this is a * normal operation. */ - if (p != NULL && p[strlen(p) - 1] != '/') { + if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); @@ -689,17 +714,9 @@ archive_write_pax_header(struct archive_write *a, } break; } - case AE_IFSOCK: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive socket"); - return (ARCHIVE_FAILED); - default: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive this (type=0%lo)", - (unsigned long) - archive_entry_filetype(entry_original)); + default: /* AE_IFSOCK and unknown */ + __archive_write_entry_filetype_unsupported( + &a->archive, entry_original, "pax"); return (ARCHIVE_FAILED); } } @@ -835,13 +852,16 @@ archive_write_pax_header(struct archive_write *a, * them do. */ r = get_entry_pathname(a, entry_main, &path, &path_length, sconv); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); - else if (r != ARCHIVE_OK) { + } else if (r != ARCHIVE_OK) { r = get_entry_pathname(a, entry_main, &path, &path_length, NULL); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); + } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", path, archive_string_conversion_charset_name(sconv)); @@ -849,12 +869,15 @@ archive_write_pax_header(struct archive_write *a, sconv = NULL;/* The header charset switches to binary mode. */ } r = get_entry_uname(a, entry_main, &uname, &uname_length, sconv); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); - else if (r != ARCHIVE_OK) { + } else if (r != ARCHIVE_OK) { r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); + } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to %s", uname, archive_string_conversion_charset_name(sconv)); @@ -862,12 +885,15 @@ archive_write_pax_header(struct archive_write *a, sconv = NULL;/* The header charset switches to binary mode. */ } r = get_entry_gname(a, entry_main, &gname, &gname_length, sconv); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); - else if (r != ARCHIVE_OK) { + } else if (r != ARCHIVE_OK) { r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); + } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to %s", gname, archive_string_conversion_charset_name(sconv)); @@ -879,13 +905,16 @@ archive_write_pax_header(struct archive_write *a, if (linkpath == NULL) { r = get_entry_symlink(a, entry_main, &linkpath, &linkpath_length, sconv); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); - else if (r != ARCHIVE_OK) { + } else if (r != ARCHIVE_OK) { r = get_entry_symlink(a, entry_main, &linkpath, &linkpath_length, NULL); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); + } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", linkpath, @@ -901,21 +930,29 @@ archive_write_pax_header(struct archive_write *a, if (hardlink != NULL) { r = get_entry_hardlink(a, entry_main, &hardlink, &hardlink_length, NULL); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); + } linkpath = hardlink; linkpath_length = hardlink_length; } r = get_entry_pathname(a, entry_main, &path, &path_length, NULL); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); + } r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); + } r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL); - if (r == ARCHIVE_FATAL) + if (r == ARCHIVE_FATAL) { + archive_entry_free(entry_main); return (r); + } } /* Store the header encoding first, to be nice to readers. */ @@ -1112,6 +1149,10 @@ archive_write_pax_header(struct archive_write *a, if (!need_extension && acl_types != 0) need_extension = 1; + /* If the symlink type is defined, we need an extension */ + if (!need_extension && archive_entry_symlink_type(entry_main) > 0) + need_extension = 1; + /* * Libarchive used to include these in extended headers for * restricted pax format, but that confused people who @@ -1168,24 +1209,33 @@ archive_write_pax_header(struct archive_write *a, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA | ARCHIVE_ENTRY_ACL_STYLE_COMPACT); - if (ret == ARCHIVE_FATAL) + if (ret == ARCHIVE_FATAL) { + archive_entry_free(entry_main); + archive_string_free(&entry_name); return (ARCHIVE_FATAL); + } } if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { ret = add_pax_acl(a, entry_original, pax, ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA); - if (ret == ARCHIVE_FATAL) + if (ret == ARCHIVE_FATAL) { + archive_entry_free(entry_main); + archive_string_free(&entry_name); return (ARCHIVE_FATAL); + } } if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) { ret = add_pax_acl(a, entry_original, pax, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT | ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA); - if (ret == ARCHIVE_FATAL) + if (ret == ARCHIVE_FATAL) { + archive_entry_free(entry_main); + archive_string_free(&entry_name); return (ARCHIVE_FATAL); + } } /* We use GNU-tar-compatible sparse attributes. */ @@ -1245,6 +1295,17 @@ archive_write_pax_header(struct archive_write *a, archive_string_free(&entry_name); return (ARCHIVE_FATAL); } + + /* Store extended symlink information */ + if (archive_entry_symlink_type(entry_main) == + AE_SYMLINK_TYPE_FILE) { + add_pax_attr(&(pax->pax_header), + "LIBARCHIVE.symlinktype", "file"); + } else if (archive_entry_symlink_type(entry_main) == + AE_SYMLINK_TYPE_DIRECTORY) { + add_pax_attr(&(pax->pax_header), + "LIBARCHIVE.symlinktype", "dir"); + } } /* Only regular files have data. */ @@ -1313,8 +1374,11 @@ archive_write_pax_header(struct archive_write *a, * numeric fields, though they're less critical. */ if (__archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0, - NULL) == ARCHIVE_FATAL) + NULL) == ARCHIVE_FATAL) { + archive_entry_free(entry_main); + archive_string_free(&entry_name); return (ARCHIVE_FATAL); + } /* If we built any extended attributes, write that entry first. */ if (archive_strlen(&(pax->pax_header)) > 0) { @@ -1379,6 +1443,8 @@ archive_write_pax_header(struct archive_write *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "archive_write_pax_header: " "'x' header failed?! This can't happen.\n"); + archive_entry_free(entry_main); + archive_string_free(&entry_name); return (ARCHIVE_FATAL); } else if (r < ret) ret = r; @@ -1387,6 +1453,8 @@ archive_write_pax_header(struct archive_write *a, sparse_list_clear(pax); pax->entry_bytes_remaining = 0; pax->entry_padding = 0; + archive_entry_free(entry_main); + archive_string_free(&entry_name); return (ARCHIVE_FATAL); } @@ -1398,12 +1466,16 @@ archive_write_pax_header(struct archive_write *a, archive_strlen(&(pax->pax_header))); if (r != ARCHIVE_OK) { /* If a write fails, we're pretty much toast. */ + archive_entry_free(entry_main); + archive_string_free(&entry_name); return (ARCHIVE_FATAL); } /* Pad out the end of the entry. */ r = __archive_write_nulls(a, (size_t)pax->entry_padding); if (r != ARCHIVE_OK) { /* If a write fails, we're pretty much toast. */ + archive_entry_free(entry_main); + archive_string_free(&entry_name); return (ARCHIVE_FATAL); } pax->entry_bytes_remaining = pax->entry_padding = 0; @@ -1411,8 +1483,11 @@ archive_write_pax_header(struct archive_write *a, /* Write the header for main entry. */ r = __archive_write_output(a, ustarbuff, 512); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + archive_entry_free(entry_main); + archive_string_free(&entry_name); return (r); + } /* * Inform the client of the on-disk size we're using, so diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_private.h b/Utilities/cmlibarchive/libarchive/archive_write_set_format_private.h new file mode 100644 index 00000000000..e20022755f8 --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_private.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2020 Martin Matuska + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED +#define ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED + +#ifndef __LIBARCHIVE_BUILD +#ifndef __LIBARCHIVE_TEST +#error This header is only to be used internally to libarchive. +#endif +#endif + +#include "archive.h" +#include "archive_entry.h" + +void __archive_write_entry_filetype_unsupported(struct archive *a, + struct archive_entry *entry, const char *format); +#endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c index 5be310a0781..9e4931c95c1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_shar.c 189438 2 #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" struct shar { int dump; @@ -169,8 +170,7 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) } /* Save the entry for the closing. */ - if (shar->entry) - archive_entry_free(shar->entry); + archive_entry_free(shar->entry); shar->entry = archive_entry_clone(entry); name = archive_entry_pathname(entry); @@ -195,8 +195,8 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) archive_entry_set_size(entry, 0); if (archive_entry_hardlink(entry) == NULL && archive_entry_symlink(entry) == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "shar format cannot archive this"); + __archive_write_entry_filetype_unsupported( + &a->archive, entry, "shar"); return (ARCHIVE_WARN); } } @@ -289,8 +289,7 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) "mkdir -p %s > /dev/null 2>&1\n", shar->quoted_name.s); /* Record that we just created this directory. */ - if (shar->last_dir != NULL) - free(shar->last_dir); + free(shar->last_dir); shar->last_dir = strdup(name); /* Trim a trailing '/'. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c index c54aeabdb19..d1a06bc4f7e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ustar.c 191579 #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" struct ustar { uint64_t entry_bytes_remaining; @@ -352,14 +353,12 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) #endif ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, sconv); if (ret < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret2); } if (ret2 < ret) @@ -367,8 +366,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) ustar->entry_bytes_remaining = archive_entry_size(entry); ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining); - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } @@ -515,9 +513,11 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], } if (copy_length > 0) { if (copy_length > USTAR_uname_size) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Username too long"); - ret = ARCHIVE_FAILED; + if (tartype != 'x') { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, "Username too long"); + ret = ARCHIVE_FAILED; + } copy_length = USTAR_uname_size; } memcpy(h + USTAR_uname_offset, p, copy_length); @@ -538,9 +538,11 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], } if (copy_length > 0) { if (strlen(p) > USTAR_gname_size) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Group name too long"); - ret = ARCHIVE_FAILED; + if (tartype != 'x') { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, "Group name too long"); + ret = ARCHIVE_FAILED; + } copy_length = USTAR_gname_size; } memcpy(h + USTAR_gname_offset, p, copy_length); @@ -612,16 +614,9 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break; case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break; case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break; - case AE_IFSOCK: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive socket"); - return (ARCHIVE_FAILED); - default: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive this (mode=0%lo)", - (unsigned long)archive_entry_mode(entry)); + default: /* AE_IFSOCK and unknown */ + __archive_write_entry_filetype_unsupported( + &a->archive, entry, "ustar"); ret = ARCHIVE_FAILED; } } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c index 62b15229444..59940714412 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" struct v7tar { uint64_t entry_bytes_remaining; @@ -284,7 +285,7 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) * case getting WCS failed. On POSIX, this is a * normal operation. */ - if (p != NULL && p[strlen(p) - 1] != '/') { + if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); @@ -330,14 +331,12 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) #endif ret = format_header_v7tar(a, buff, entry, 1, sconv); if (ret < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret2); } if (ret2 < ret) @@ -345,8 +344,7 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) v7tar->entry_bytes_remaining = archive_entry_size(entry); v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining); - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } @@ -494,31 +492,11 @@ format_header_v7tar(struct archive_write *a, char h[512], case AE_IFLNK: h[V7TAR_typeflag_offset] = '2'; break; - case AE_IFCHR: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive character device"); - return (ARCHIVE_FAILED); - case AE_IFBLK: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive block device"); - return (ARCHIVE_FAILED); - case AE_IFIFO: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive fifo"); - return (ARCHIVE_FAILED); - case AE_IFSOCK: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive socket"); - return (ARCHIVE_FAILED); default: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "tar format cannot archive this (mode=0%lo)", - (unsigned long)archive_entry_mode(entry)); + /* AE_IFBLK, AE_IFCHR, AE_IFIFO, AE_IFSOCK + * and unknown */ + __archive_write_entry_filetype_unsupported( + &a->archive, entry, "v7tar"); ret = ARCHIVE_FAILED; } } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c index edad072cf77..46b05734121 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include "archive_private.h" #include "archive_random_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" struct warc_s { unsigned int omit_warcinfo:1; @@ -259,10 +260,8 @@ _warc_header(struct archive_write *a, struct archive_entry *entry) return (ARCHIVE_OK); } /* just resort to erroring as per Tim's advice */ - archive_set_error( - &a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "WARC can only process regular files"); + __archive_write_entry_filetype_unsupported( + &a->archive, entry, "WARC"); return (ARCHIVE_FAILED); } @@ -332,6 +331,10 @@ xstrftime(struct archive_string *as, const char *fmt, time_t t) struct tm *rt; #if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) struct tm timeHere; +#endif +#if defined(HAVE__GMTIME64_S) + errno_t terr; + __time64_t tmptime; #endif char strtime[100]; size_t len; @@ -340,7 +343,12 @@ xstrftime(struct archive_string *as, const char *fmt, time_t t) if ((rt = gmtime_r(&t, &timeHere)) == NULL) return; #elif defined(HAVE__GMTIME64_S) - _gmtime64_s(&timeHere, &t); + tmptime = t; + terr = _gmtime64_s(&timeHere, &tmptime); + if (terr) + rt = NULL; + else + rt = &timeHere; #else if ((rt = gmtime(&t)) == NULL) return; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c index 3c617ecd9fc..3b0ffb38a18 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c @@ -37,13 +37,13 @@ __FBSDID("$FreeBSD$"); #include #endif #ifdef HAVE_BZLIB_H -#include +#include #endif #if HAVE_LZMA_H -#include +#include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" @@ -212,8 +212,8 @@ struct file { struct heap_data data; struct archive_string script; - int virtual:1; - int dir:1; + signed int virtual:1; + signed int dir:1; }; struct hardlink { @@ -411,6 +411,8 @@ xar_options(struct archive_write *a, const char *key, const char *value) if (strcmp(key, "checksum") == 0) { if (value == NULL) xar->opt_sumalg = CKSUM_NONE; + else if (strcmp(value, "none") == 0) + xar->opt_sumalg = CKSUM_NONE; else if (strcmp(value, "sha1") == 0) xar->opt_sumalg = CKSUM_SHA1; else if (strcmp(value, "md5") == 0) @@ -429,6 +431,8 @@ xar_options(struct archive_write *a, const char *key, const char *value) if (value == NULL) xar->opt_compression = NONE; + else if (strcmp(value, "none") == 0) + xar->opt_compression = NONE; else if (strcmp(value, "gzip") == 0) xar->opt_compression = GZIP; else if (strcmp(value, "bzip2") == 0) @@ -482,6 +486,8 @@ xar_options(struct archive_write *a, const char *key, const char *value) if (strcmp(key, "toc-checksum") == 0) { if (value == NULL) xar->opt_toc_sumalg = CKSUM_NONE; + else if (strcmp(value, "none") == 0) + xar->opt_toc_sumalg = CKSUM_NONE; else if (strcmp(value, "sha1") == 0) xar->opt_toc_sumalg = CKSUM_SHA1; else if (strcmp(value, "md5") == 0) @@ -496,10 +502,13 @@ xar_options(struct archive_write *a, const char *key, const char *value) return (ARCHIVE_OK); } if (strcmp(key, "threads") == 0) { + char *endptr; + if (value == NULL) return (ARCHIVE_FAILED); - xar->opt_threads = (int)strtoul(value, NULL, 10); - if (xar->opt_threads == 0 && errno != 0) { + errno = 0; + xar->opt_threads = (int)strtoul(value, &endptr, 10); + if (errno != 0 || *endptr != '\0') { xar->opt_threads = 1; archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, @@ -693,13 +702,37 @@ xar_write_data(struct archive_write *a, const void *buff, size_t s) else run = ARCHIVE_Z_FINISH; /* Compress file data. */ - r = compression_code(&(a->archive), &(xar->stream), run); - if (r != ARCHIVE_OK && r != ARCHIVE_EOF) - return (ARCHIVE_FATAL); + for (;;) { + r = compression_code(&(a->archive), &(xar->stream), + run); + if (r != ARCHIVE_OK && r != ARCHIVE_EOF) + return (ARCHIVE_FATAL); + if (xar->stream.avail_out == 0 || + run == ARCHIVE_Z_FINISH) { + size = sizeof(xar->wbuff) - + xar->stream.avail_out; + checksum_update(&(xar->a_sumwrk), xar->wbuff, + size); + xar->cur_file->data.length += size; + if (write_to_temp(a, xar->wbuff, + size) != ARCHIVE_OK) + return (ARCHIVE_FATAL); + if (r == ARCHIVE_OK) { + /* Output buffer was full */ + xar->stream.next_out = xar->wbuff; + xar->stream.avail_out = + sizeof(xar->wbuff); + } else { + /* ARCHIVE_EOF - We are done */ + break; + } + } else { + /* Compressor wants more input */ + break; + } + } rsize = s - xar->stream.avail_in; checksum_update(&(xar->e_sumwrk), buff, rsize); - size = sizeof(xar->wbuff) - xar->stream.avail_out; - checksum_update(&(xar->a_sumwrk), xar->wbuff, size); } #if !defined(_WIN32) || defined(__CYGWIN__) if (xar->bytes_remaining == @@ -736,12 +769,9 @@ xar_write_data(struct archive_write *a, const void *buff, size_t s) if (xar->cur_file->data.compression == NONE) { if (write_to_temp(a, buff, size) != ARCHIVE_OK) return (ARCHIVE_FATAL); - } else { - if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK) - return (ARCHIVE_FATAL); + xar->cur_file->data.length += size; } xar->bytes_remaining -= rsize; - xar->cur_file->data.length += size; return (rsize); } @@ -875,11 +905,15 @@ xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer, { char timestr[100]; struct tm tm; +#if defined(HAVE__GMTIME64_S) + __time64_t tmptime; +#endif #if defined(HAVE_GMTIME_R) gmtime_r(&t, &tm); #elif defined(HAVE__GMTIME64_S) - _gmtime64_s(&tm, &t); + tmptime = t; + _gmtime64_s(&tm, &tmptime); #else memcpy(&tm, gmtime(&t), sizeof(tm)); #endif @@ -2100,7 +2134,7 @@ file_gen_utility_names(struct archive_write *a, struct file *file) while (len > 0) { size_t ll = len; - if (len > 0 && p[len-1] == '/') { + if (p[len-1] == '/') { p[len-1] = '\0'; len--; } @@ -2120,10 +2154,10 @@ file_gen_utility_names(struct archive_write *a, struct file *file) if (p[0] == '/') { if (p[1] == '/') /* Convert '//' --> '/' */ - strcpy(p, p+1); + memmove(p, p+1, strlen(p+1) + 1); else if (p[1] == '.' && p[2] == '/') /* Convert '/./' --> '/' */ - strcpy(p, p+2); + memmove(p, p+2, strlen(p+2) + 1); else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { /* Convert 'dir/dir1/../dir2/' * --> 'dir/dir2/' @@ -2529,13 +2563,11 @@ file_init_hardlinks(struct xar *xar) static void file_free_hardlinks(struct xar *xar) { - struct archive_rb_node *n, *next; + struct archive_rb_node *n, *tmp; - for (n = ARCHIVE_RB_TREE_MIN(&(xar->hardlink_rbtree)); n;) { - next = __archive_rb_tree_iterate(&(xar->hardlink_rbtree), - n, ARCHIVE_RB_DIR_RIGHT); + ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(xar->hardlink_rbtree), tmp) { + __archive_rb_tree_remove_node(&(xar->hardlink_rbtree), n); free(n); - n = next; } } @@ -3169,8 +3201,10 @@ save_xattrs(struct archive_write *a, struct file *file) checksum_update(&(xar->a_sumwrk), xar->wbuff, size); if (write_to_temp(a, xar->wbuff, size) - != ARCHIVE_OK) + != ARCHIVE_OK) { + free(heap); return (ARCHIVE_FATAL); + } if (r == ARCHIVE_OK) { xar->stream.next_out = xar->wbuff; xar->stream.avail_out = sizeof(xar->wbuff); diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c index a4ae229b2f5..b0cd215b87d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c @@ -45,7 +45,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_zip.c 201168 20 #include #endif #ifdef HAVE_ZLIB_H -#include +#include #endif #include "archive.h" @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_zip.c 201168 20 #include "archive_private.h" #include "archive_random_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" @@ -526,8 +527,8 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) /* Ignore types of entries that we don't support. */ type = archive_entry_filetype(entry); if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Filetype not supported"); + __archive_write_entry_filetype_unsupported( + &a->archive, entry, "zip"); return ARCHIVE_FAILED; }; @@ -564,10 +565,8 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) zip->entry_uses_zip64 = 0; zip->entry_crc32 = zip->crc32func(0, NULL, 0); zip->entry_encryption = 0; - if (zip->entry != NULL) { - archive_entry_free(zip->entry); - zip->entry = NULL; - } + archive_entry_free(zip->entry); + zip->entry = NULL; if (zip->cctx_valid) archive_encrypto_aes_ctr_release(&zip->cctx); @@ -1374,10 +1373,28 @@ dos_time(const time_t unix_time) { struct tm *t; unsigned int dt; +#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) + struct tm tmbuf; +#endif +#if defined(HAVE__LOCALTIME64_S) + errno_t terr; + __time64_t tmptime; +#endif /* This will not preserve time when creating/extracting the archive * on two systems with different time zones. */ +#if defined(HAVE_LOCALTIME_R) + t = localtime_r(&unix_time, &tmbuf); +#elif defined(HAVE__LOCALTIME64_S) + tmptime = unix_time; + terr = _localtime64_s(&tmbuf, &tmptime); + if (terr) + t = NULL; + else + t = &tmbuf; +#else t = localtime(&unix_time); +#endif /* MSDOS-style date/time is only between 1980-01-01 and 2107-12-31 */ if (t->tm_year < 1980 - 1900) @@ -1404,18 +1421,17 @@ path_length(struct archive_entry *entry) { mode_t type; const char *path; + size_t len; type = archive_entry_filetype(entry); path = archive_entry_pathname(entry); if (path == NULL) return (0); - if (type == AE_IFDIR && - (path[0] == '\0' || path[strlen(path) - 1] != '/')) { - return strlen(path) + 1; - } else { - return strlen(path); - } + len = strlen(path); + if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/')) + ++len; /* Space for the trailing / */ + return len; } static int @@ -1430,6 +1446,9 @@ write_path(struct archive_entry *entry, struct archive_write *archive) type = archive_entry_filetype(entry); written_bytes = 0; + if (path == NULL) + return (ARCHIVE_FATAL); + ret = __archive_write_output(archive, path, strlen(path)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -1460,10 +1479,8 @@ copy_path(struct archive_entry *entry, unsigned char *p) memcpy(p, path, pathlen); /* Folders are recognized by a trailing slash. */ - if ((type == AE_IFDIR) & (path[pathlen - 1] != '/')) { + if ((type == AE_IFDIR) && (path[pathlen - 1] != '/')) p[pathlen] = '/'; - p[pathlen + 1] = '\0'; - } } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 index aeb7a184865..cffe571e90a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 2, 2012 +.Dd January 31, 2020 .Dt ARCHIVE_WRITE_OPTIONS 3 .Os .Sh NAME @@ -70,7 +70,7 @@ specific write modules. .Fn archive_write_set_filter_option , .Fn archive_write_set_format_option .Xc -Specifies an option that will be passed to currently-registered +Specifies an option that will be passed to the currently-registered filters (including decompression filters) or format readers. .Pp If @@ -138,7 +138,7 @@ If either function returns .Cm ARCHIVE_FATAL will be returned immediately. -Otherwise, greater of the two values will be returned. +Otherwise, the greater of the two values will be returned. .\" .It Fn archive_write_set_options .Ar options @@ -170,55 +170,153 @@ only to modules whose name matches .\" .Sh OPTIONS .Bl -tag -compact -width indent +.It Filter b64encode +.Bl -tag -compact -width indent +.It Cm mode +The value is interpreted as octal digits specifying the file mode. +.It Cm name +The value specifies the file name. +.El +.It Filter bzip2 +.Bl -tag -compact -width indent +.It Cm compression-level +The value is interpreted as a decimal integer specifying the +bzip2 compression level. Supported values are from 1 to 9. +.El .It Filter gzip .Bl -tag -compact -width indent .It Cm compression-level The value is interpreted as a decimal integer specifying the -gzip compression level. +gzip compression level. Supported values are from 0 to 9. +.It Cm timestamp +Store timestamp. This is enabled by default. +.El +.It Filter lrzip +.Bl -tag -compact -width indent +.It Cm compression Ns = Ns Ar type +Use +.Ar type +as compression method. +Supported values are +.Dq bzip2 , +.Dq gzipi , +.Dq lzo +.Pq ultra fast , +and +.Dq zpaq +.Pq best, extremely slow . +.It Cm compression-level +The value is interpreted as a decimal integer specifying the +lrzip compression level. Supported values are from 1 to 9. +.El +.It Filter lz4 +.Bl -tag -compact -width indent +.It Cm compression-level +The value is interpreted as a decimal integer specifying the +lz4 compression level. Supported values are from 0 to 9. +.It Cm stream-checksum +Enable stream checksum. This is enabled by default. +.It Cm block-checksum +Enable block checksum. This is disabled by default. +.It Cm block-size +The value is interpreted as a decimal integer specifying the +lz4 compression block size. Supported values are from 4 to 7 +.Pq default . +.It Cm block-dependence +Use the previous block of the block being compressed for +a compression dictionary to improve compression ratio. +This is disabled by default. +.El +.It Filter lzop +.Bl -tag -compact -width indent +.It Cm compression-level +The value is interpreted as a decimal integer specifying the +lzop compression level. Supported values are from 1 to 9. +.El +.It Filter uuencode +.Bl -tag -compact -width indent +.It Cm mode +The value is interpreted as octal digits specifying the file mode. +.It Cm name +The value specifies the file name. .El .It Filter xz .Bl -tag -compact -width indent .It Cm compression-level The value is interpreted as a decimal integer specifying the +compression level. Supported values are from 0 to 9. +.It Cm threads +The value is interpreted as a decimal integer specifying the +number of threads for multi-threaded lzma compression. +If supported, the default value is read from +.Fn lzma_cputhreads . +.El +.It Filter zstd +.Bl -tag -compact -width indent +.It Cm compression-level +The value is interpreted as a decimal integer specifying the +compression level. Supported values are from 1 to 22. +.El +.It Format 7zip +.Bl -tag -compact -width indent +.It Cm compression +The value is one of +.Dq store , +.Dq deflate , +.Dq bzip2 , +.Dq lzma1 , +.Dq lzma2 +or +.Dq ppmd +to indicate how the following entries should be compressed. +Note that this setting is ignored for directories, symbolic links, +and other special entries. +.It Cm compression-level +The value is interpreted as a decimal integer specifying the compression level. +Values between 0 and 9 are supported. +The interpretation of the compression level depends on the chosen +compression method. .El -.It Format mtree +.It Format cpio .Bl -tag -compact -width indent -.It Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname -Enable a particular keyword in the mtree output. -Prefix with an exclamation mark to disable the corresponding keyword. -The default is equivalent to -.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname . -.It Cm all -Enables all of the above keywords. -.It Cm use-set -Enables generation of -.Cm /set -lines that specify default values for the following files and/or directories. -.It Cm indent -XXX needs explanation XXX +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file names. +.El +.It Format gnutar +.Bl -tag -compact -width indent +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file, group and user names. .El .It Format iso9660 - volume metadata These options are used to set standard ISO9660 metadata. .Bl -tag -compact -width indent .It Cm abstract-file Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata -as holding the abstract for this volume. Default: none. +as holding the abstract for this volume. +Default: none. .It Cm application-id Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata -as holding the application identifier for this volume. Default: none. +as holding the application identifier for this volume. +Default: none. .It Cm biblio-file Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata -as holding the bibliography for this volume. Default: none. +as holding the bibliography for this volume. +Default: none. .It Cm copyright-file Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata -as holding the copyright for this volume. Default: none. +as holding the copyright for this volume. +Default: none. .It Cm publisher Ns = Ns Ar filename The file with the specified name will be identified in the ISO9660 metadata -as holding the publisher information for this volume. Default: none. +as holding the publisher information for this volume. +Default: none. .It Cm volume-id Ns = Ns Ar string The specified string will be used as the Volume Identifier in the ISO9660 metadata. -It is limited to 32 bytes. Default: none. +It is limited to 32 bytes. +Default: none. .El .It Format iso9660 - boot support These options are used to make an ISO9660 image that can be directly @@ -266,7 +364,7 @@ If the boot image is exactly 1.2MB, 1.44MB, or 2.88MB, then the default is .Cm fd , otherwise the default is -.Cm no-emulation. +.Cm no-emulation . .El .It Format iso9660 - filename and size extensions Various extensions to the base ISO9660 format. @@ -290,7 +388,7 @@ This does not impact names stored in the Rockridge or Joliet extension area. Default: disabled. .It Cm allow-period If enabled, allows filenames to contain trailing period characters, in violation of the ISO9660 specification. -If disabled,trailing periods will be converted to underscore characters. +If disabled, trailing periods will be converted to underscore characters. This does not impact names stored in the Rockridge or Joliet extension area. Default: disabled. .It Cm allow-pvd-lowercase @@ -398,6 +496,111 @@ Specifies a filename that should not be compressed when using This option can be provided multiple times to suppress compression on many files. .El +.It Format mtree +.Bl -tag -compact -width indent +.It Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname +Enable a particular keyword in the mtree output. +Prefix with an exclamation mark to disable the corresponding keyword. +The default is equivalent to +.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname . +.It Cm all +Enables all of the above keywords. +.It Cm use-set +Enables generation of +.Cm /set +lines that specify default values for the following files and/or directories. +.It Cm indent +XXX needs explanation XXX +.El +.It Format newc +.Bl -tag -compact -width indent +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file names. +.El +.It Format pax +.Bl -tag -compact -width indent +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file, group and user names. +The value is one of +.Dq BINARY +or +.Dq UTF-8 . +With +.Dq BINARY +there is no character conversion, with +.Dq UTF-8 +names are converted to UTF-8. +.It Cm xattrheader +When storing extended attributes, this option configures which +headers should be written. The value is one of +.Dq all , +.Dq LIBARCHIVE , +or +.Dq SCHILY . +By default, both +.Dq LIBARCHIVE.xattr +and +.Dq SCHILY.xattr +headers are written. +.El +.It Format ustar +.Bl -tag -compact -width indent +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file, group and user names. +.El +.It Format v7tar +.Bl -tag -compact -width indent +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file, group and user names. +.El +.It Format warc +.Bl -tag -compact -width indent +.It Cm omit-warcinfo +Set to +.Dq true +to disable output of the warcinfo record. +.El +.It Format xar +.Bl -tag -compact -width indent +.It Cm checksum Ns = Ns Ar type +Use +.Ar type +as file checksum method. +Supported values are +.Dq none , +.Dq md5 , +and +.Dq sha1 +.Pq default . +.It Cm compression Ns = Ns Ar type +Use +.Ar type +as compression method. +Supported values are +.Dq none , +.Dq bzip2 , +.Dq gzip +.Pq default , +.Dq lzma +and +.Dq xz . +.It Cm compression_level +The value is a decimal integer from 1 to 9 specifying the compression level. +.It Cm toc-checksum Ns = Ns Ar type +Use +.Ar type +as table of contents checksum method. +Supported values are +.Dq none , +.Dq md5 +and +.Dq sha1 +.Pq default . +.El .It Format zip .Bl -tag -compact -width indent .It Cm compression @@ -408,6 +611,29 @@ or to indicate how the following entries should be compressed. Note that this setting is ignored for directories, symbolic links, and other special entries. +.It Cm compression-level +The value is interpreted as a decimal integer specifying the +compression level. +Values between 0 and 9 are supported. +A compression level of 0 switches the compression method to +.Dq store , +other values will enable +.Dq deflate +compression with the given level. +.It Cm encryption +Enable encryption using traditional zip encryption. +.It Cm encryption Ns = Ns Ar type +Use +.Ar type +as encryption type. +Supported values are +.Dq zipcrypt +.Pq traditional zip encryption , +.Dq aes128 +.Pq WinZip AES-128 encryption +and +.Dq aes256 +.Pq WinZip AES-256 encryption . .It Cm experimental This boolean option enables or disables experimental Zip features that may not be compatible with other Zip implementations. @@ -416,7 +642,8 @@ This boolean option disables CRC calculations. All CRC fields are set to zero. It should not be used except for testing purposes. .It Cm hdrcharset -This sets the character set used for filenames. +The value is used as a character set name that will be +used when translating file names. .It Cm zip64 Zip64 extensions provide additional file size information for entries larger than 4 GiB. @@ -465,9 +692,9 @@ functions. .\" .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_read_set_options 3 , -.Xr archive_write 3 +.Xr archive_write 3 , +.Xr libarchive 3 .Sh HISTORY The .Nm libarchive diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.3 b/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.3 index 2585595e331..2db77034c76 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.3 @@ -49,7 +49,7 @@ Streaming Archive Library (libarchive, -larchive) .Sh DESCRIPTION .Bl -tag -width indent .It Fn archive_write_set_passphrase -Set a passphrase for writing an encryption archive. +Set a passphrase for writing an encrypted archive. If .Ar passphrase is @@ -59,16 +59,16 @@ or empty, this function will do nothing and will be returned. Otherwise, .Cm ARCHIVE_OK -will be returned. +will be returned. .It Fn archive_write_set_passphrase_callback -Register callback function that will be invoked to get a passphrase -for encrption if the passphrase was not set by the +Register a callback function that will be invoked to get a passphrase +for encryption if the passphrase was not set by the .Fn archive_write_set_passphrase function. .El .\" .Sh ERRORS .Sh SEE ALSO .Xr tar 1 , -.Xr libarchive 3 , .Xr archive_write 3 , -.Xr archive_write_set_options 3 +.Xr archive_write_set_options 3 , +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/archive_xxhash.h b/Utilities/cmlibarchive/libarchive/archive_xxhash.h index 427241641a0..1c7131ca1e7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_xxhash.h +++ b/Utilities/cmlibarchive/libarchive/archive_xxhash.h @@ -24,12 +24,13 @@ * */ +#ifndef ARCHIVE_XXHASH_H_INCLUDED +#define ARCHIVE_XXHASH_H_INCLUDED + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef ARCHIVE_XXHASH_H -#define ARCHIVE_XXHASH_H typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; diff --git a/Utilities/cmlibarchive/libarchive/config_freebsd.h b/Utilities/cmlibarchive/libarchive/config_freebsd.h index be25258f946..f16fd3495ed 100644 --- a/Utilities/cmlibarchive/libarchive/config_freebsd.h +++ b/Utilities/cmlibarchive/libarchive/config_freebsd.h @@ -210,6 +210,7 @@ #define HAVE_TZSET 1 #define HAVE_UINTMAX_T 1 #define HAVE_UNISTD_H 1 +#define HAVE_UNLINKAT 1 #define HAVE_UNSETENV 1 #define HAVE_UNSIGNED_LONG_LONG 1 #define HAVE_UNSIGNED_LONG_LONG_INT 1 diff --git a/Utilities/cmlibarchive/libarchive/filter_fork.h b/Utilities/cmlibarchive/libarchive/filter_fork.h index a28272bee33..908e7cdd4dd 100644 --- a/Utilities/cmlibarchive/libarchive/filter_fork.h +++ b/Utilities/cmlibarchive/libarchive/filter_fork.h @@ -25,13 +25,13 @@ * $FreeBSD: head/lib/libarchive/filter_fork.h 201087 2009-12-28 02:18:26Z kientzle $ */ +#ifndef FILTER_FORK_H +#define FILTER_FORK_H + #ifndef __LIBARCHIVE_BUILD #error This header is only to be used internally to libarchive. #endif -#ifndef FILTER_FORK_H -#define FILTER_FORK_H - pid_t __archive_create_child(const char *cmd, int *child_stdin, int *child_stdout); diff --git a/Utilities/cmlibarchive/libarchive/libarchive_changes.3 b/Utilities/cmlibarchive/libarchive/libarchive_changes.3 index adc87febd71..6bf8db038c7 100644 --- a/Utilities/cmlibarchive/libarchive/libarchive_changes.3 +++ b/Utilities/cmlibarchive/libarchive/libarchive_changes.3 @@ -35,7 +35,6 @@ This page describes user-visible changes in libarchive3, and lists public functions and other symbols changed, deprecated or removed in libarchive3, along with their replacements if any. -.Pp .\" .Ss Multiple Filters .\" @@ -330,13 +329,13 @@ or .Li 10240 .El .Sh SEE ALSO -.Xr libarchive 3 , .Xr archive_read 3 , .Xr archive_read_filter 3 , .Xr archive_read_format 3 , .Xr archive_read_set_options 3 , +.Xr archive_util 3 , .Xr archive_write 3 , .Xr archive_write_filter 3 , .Xr archive_write_format 3 , .Xr archive_write_set_options 3 , -.Xr archive_util 3 +.Xr libarchive 3 diff --git a/Utilities/cmlibarchive/libarchive/libarchive_internals.3 b/Utilities/cmlibarchive/libarchive/libarchive_internals.3 index 8275d66e68f..d672f3e8a64 100644 --- a/Utilities/cmlibarchive/libarchive/libarchive_internals.3 +++ b/Utilities/cmlibarchive/libarchive/libarchive_internals.3 @@ -350,8 +350,8 @@ as a dedicated ZIP program. .Xr archive_entry 3 , .Xr archive_read 3 , .Xr archive_write 3 , -.Xr archive_write_disk 3 -.Xr libarchive 3 , +.Xr archive_write_disk 3 , +.Xr libarchive 3 .Sh HISTORY The .Nm libarchive diff --git a/Utilities/cmlibarchive/libarchive/mtree.5 b/Utilities/cmlibarchive/libarchive/mtree.5 index e607e4a8197..8147796f310 100644 --- a/Utilities/cmlibarchive/libarchive/mtree.5 +++ b/Utilities/cmlibarchive/libarchive/mtree.5 @@ -133,7 +133,6 @@ or .Sy char file types. The value must be one of the following forms: -.Pp .Bl -tag -width 4n .It Ar format , Ns Ar major , Ns Ar minor Ns Bo , Ns Ar subunit Bc A device with @@ -165,8 +164,8 @@ are recognized: .Sy solaris , .Sy sunos , .Sy svr3 , -.Sy svr4 , -and +.Sy svr4 , +and .Sy ultrix . .Pp See @@ -288,12 +287,10 @@ The file owner as a numeric value. .It Cm uname The file owner as a symbolic name. .El -.Pp .Sh SEE ALSO .Xr cksum 1 , .Xr find 1 , .Xr mtree 8 -.Sh BUGS .Sh HISTORY The .Nm diff --git a/Utilities/cmlibarchive/libarchive/tar.5 b/Utilities/cmlibarchive/libarchive/tar.5 index 30b837dc413..34ad4f79315 100644 --- a/Utilities/cmlibarchive/libarchive/tar.5 +++ b/Utilities/cmlibarchive/libarchive/tar.5 @@ -441,7 +441,7 @@ archives to store files much larger than the historic 8GB limit. Vendor-specific attributes used by Joerg Schilling's .Nm star implementation. -.It Cm SCHILY.acl.access , Cm SCHILY.acl.default, Cm SCHILY.acl.ace +.It Cm SCHILY.acl.access , Cm SCHILY.acl.default , Cm SCHILY.acl.ace Stores the access, default and NFSv4 ACLs as textual strings in a format that is an extension of the format specified by POSIX.1e draft 17. In particular, each user or group access specification can include @@ -456,7 +456,7 @@ The file flags. .It Cm SCHILY.realsize The full size of the file on disk. XXX explain? XXX -.It Cm SCHILY.dev, Cm SCHILY.ino , Cm SCHILY.nlinks +.It Cm SCHILY.dev , Cm SCHILY.ino , Cm SCHILY.nlinks The device number, inode number, and link count for the entry. In particular, note that a pax interchange format archive using Joerg Schilling's @@ -473,7 +473,7 @@ The time when the file was created. .Dq ctime attribute, which refers to the time when the file metadata was last changed.) -.It Cm LIBARCHIVE.xattr. Ns Ar namespace Ns . Ns Ar key +.It Cm LIBARCHIVE.xattr . Ns Ar namespace . Ns Ar key Libarchive stores POSIX.1e-style extended attributes using keys of this form. The @@ -890,7 +890,8 @@ GNU tar long pathname for the following header. .It Cm M GNU tar multivolume marker, indicating the file is a continuation of a file from the previous volume. .It Cm N -GNU tar long filename support. Deprecated. +GNU tar long filename support. +Deprecated. .It Cm S GNU tar sparse regular file. .It Cm V diff --git a/Utilities/cmliblzma/CMakeLists.txt b/Utilities/cmliblzma/CMakeLists.txt index b443fd6bcde..6b6fae63ca0 100644 --- a/Utilities/cmliblzma/CMakeLists.txt +++ b/Utilities/cmliblzma/CMakeLists.txt @@ -7,11 +7,10 @@ include(CheckTypeSize) include(TestBigEndian) CHECK_INCLUDE_FILE(byteswap.h HAVE_BYTESWAP_H) -CHECK_INCLUDE_FILE(inttypes.h HAVE_INTTYPES_H) CHECK_INCLUDE_FILE(limits.h HAVE_LIMITS_H) CHECK_INCLUDE_FILE(memory.h HAVE_MEMORY_H) CHECK_INCLUDE_FILE(strings.h HAVE_STRINGS_H) -CHECK_INCLUDE_FILE(string.h HAVE_STRING_H) +set(HAVE_STRING_H 1) CHECK_INCLUDE_FILE(sys/sysctl.h HAVE_SYS_SYSCTL_H) CHECK_INCLUDE_FILE(stdbool.h HAVE_STDBOOL_H) @@ -60,40 +59,6 @@ set(HAVE_MF_BT4 1) set(HAVE_MF_HC3 1) set(HAVE_MF_HC4 1) -CHECK_TYPE_SIZE(int16_t INT16_T) -CHECK_TYPE_SIZE(int32_t INT32_T) -CHECK_TYPE_SIZE(int64_t INT64_T) -CHECK_TYPE_SIZE(intmax_t INTMAX_T) -CHECK_TYPE_SIZE(uint8_t UINT8_T) -CHECK_TYPE_SIZE(uint16_t UINT16_T) -CHECK_TYPE_SIZE(uint32_t UINT32_T) -CHECK_TYPE_SIZE(uint64_t UINT64_T) -CHECK_TYPE_SIZE(uintmax_t UINTMAX_T) - -CHECK_TYPE_SIZE("short" SIZE_OF_SHORT) -CHECK_TYPE_SIZE("int" SIZE_OF_INT) -CHECK_TYPE_SIZE("long" SIZE_OF_LONG) -CHECK_TYPE_SIZE("long long" SIZE_OF_LONG_LONG) - -CHECK_TYPE_SIZE("unsigned short" SIZE_OF_UNSIGNED_SHORT) -CHECK_TYPE_SIZE("unsigned" SIZE_OF_UNSIGNED) -CHECK_TYPE_SIZE("unsigned long" SIZE_OF_UNSIGNED_LONG) -CHECK_TYPE_SIZE("unsigned long long" SIZE_OF_UNSIGNED_LONG_LONG) -CHECK_TYPE_SIZE("size_t" SIZEOF_SIZE_T) - -CHECK_TYPE_SIZE("__int64" __INT64) -CHECK_TYPE_SIZE("unsigned __int64" UNSIGNED___INT64) - -CHECK_TYPE_SIZE(uintptr_t UINTPTR_T) -IF(NOT HAVE_UINTPTR_T) - IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8) - SET(uintptr_t "uint64_t") - ELSE() - SET(uintptr_t "uint32_t") - ENDIF() -ENDIF() - - SET(LZMA_SRCS common/sysdefs.h common/tuklib_integer.h diff --git a/Utilities/cmliblzma/config.h.in b/Utilities/cmliblzma/config.h.in index 06f7fcb37e8..20916cacb14 100644 --- a/Utilities/cmliblzma/config.h.in +++ b/Utilities/cmliblzma/config.h.in @@ -3,183 +3,36 @@ * Ensure we have C99-style int64_t, etc, all defined. */ -/* First, we need to know if the system has already defined them. */ -#cmakedefine HAVE_INT16_T -#cmakedefine HAVE_INT32_T -#cmakedefine HAVE_INT64_T -#cmakedefine HAVE_INTMAX_T +#include -#cmakedefine HAVE_UINT8_T -#cmakedefine HAVE_UINT16_T -#cmakedefine HAVE_UINT32_T -#cmakedefine HAVE_UINT64_T -#cmakedefine HAVE_UINTMAX_T - -/* We might have the types we want under other spellings. */ -#cmakedefine HAVE___INT64 -#cmakedefine HAVE_U_INT64_T -#cmakedefine HAVE_UNSIGNED___INT64 - -/* The sizes of various standard integer types. */ -@SIZE_OF_SHORT_CODE@ -@SIZE_OF_INT_CODE@ -@SIZE_OF_LONG_CODE@ -@SIZE_OF_LONG_LONG_CODE@ -@SIZE_OF_UNSIGNED_SHORT_CODE@ -@SIZE_OF_UNSIGNED_CODE@ -@SIZE_OF_UNSIGNED_LONG_CODE@ -@SIZE_OF_UNSIGNED_LONG_LONG_CODE@ -@SIZEOF_SIZE_T_CODE@ - -/* - * If we lack int64_t, define it to the first of __int64, int, long, and long long - * that exists and is the right size. - */ -#if !defined(HAVE_INT64_T) && defined(HAVE___INT64) -typedef __int64 int64_t; -#define HAVE_INT64_T -#endif - -#if !defined(HAVE_INT64_T) && SIZE_OF_INT == 8 -typedef int int64_t; -#define HAVE_INT64_T -#endif - -#if !defined(HAVE_INT64_T) && SIZE_OF_LONG == 8 -typedef long int64_t; -#define HAVE_INT64_T -#endif - -#if !defined(HAVE_INT64_T) && SIZE_OF_LONG_LONG == 8 -typedef long long int64_t; -#define HAVE_INT64_T -#endif - -#if !defined(HAVE_INT64_T) -#error No 64-bit integer type was found. -#endif - -/* - * Similarly for int32_t - */ -#if !defined(HAVE_INT32_T) && SIZE_OF_INT == 4 -typedef int int32_t; -#define HAVE_INT32_T -#endif - -#if !defined(HAVE_INT32_T) && SIZE_OF_LONG == 4 -typedef long int32_t; -#define HAVE_INT32_T +#ifndef KWIML_INT_HAVE_INT64_T +typedef KWIML_INT_int64_t int64_t; #endif - -#if !defined(HAVE_INT32_T) -#error No 32-bit integer type was found. +#ifndef KWIML_INT_HAVE_INT32_T +typedef KWIML_INT_int32_t int32_t; #endif - -/* - * Similarly for int16_t - */ -#if !defined(HAVE_INT16_T) && SIZE_OF_INT == 2 -typedef int int16_t; -#define HAVE_INT16_T +#ifndef KWIML_INT_HAVE_INT16_T +typedef KWIML_INT_int16_t int16_t; #endif - -#if !defined(HAVE_INT16_T) && SIZE_OF_SHORT == 2 -typedef short int16_t; -#define HAVE_INT16_T +#ifndef KWIML_INT_HAVE_INT8_T +typedef KWIML_INT_int8_t int8_t; #endif - -#if !defined(HAVE_INT16_T) -#error No 16-bit integer type was found. +#ifndef KWIML_INT_HAVE_UINT64_T +typedef KWIML_INT_uint64_t uint64_t; #endif - -/* - * Similarly for uint64_t - */ -#if !defined(HAVE_UINT64_T) && defined(HAVE_UNSIGNED___INT64) -typedef unsigned __int64 uint64_t; -#define HAVE_UINT64_T +#ifndef KWIML_INT_HAVE_UINT32_T +typedef KWIML_INT_uint32_t uint32_t; #endif - -#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED == 8 -typedef unsigned uint64_t; -#define HAVE_UINT64_T +#ifndef KWIML_INT_HAVE_UINT16_T +typedef KWIML_INT_uint16_t uint16_t; #endif - -#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG == 8 -typedef unsigned long uint64_t; -#define HAVE_UINT64_T +#ifndef KWIML_INT_HAVE_UINT8_T +typedef KWIML_INT_uint8_t uint8_t; #endif - -#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG_LONG == 8 -typedef unsigned long long uint64_t; -#define HAVE_UINT64_T +#ifndef KWIML_INT_HAVE_UINTPTR_T +typedef KWIML_INT_uintptr_t uintptr_t; #endif -#if !defined(HAVE_UINT64_T) -#error No 64-bit unsigned integer type was found. -#endif - -/* - * Similarly for uint32_t - */ -#if !defined(HAVE_UINT32_T) && SIZE_OF_UNSIGNED == 4 -typedef unsigned uint32_t; -#define HAVE_UINT32_T -#endif - -#if !defined(HAVE_UINT32_T) && SIZE_OF_UNSIGNED_LONG == 4 -typedef unsigned long uint32_t; -#define HAVE_UINT32_T -#endif - -#if !defined(HAVE_UINT32_T) -#error No 32-bit unsigned integer type was found. -#endif - -/* - * Similarly for uint16_t - */ -#if !defined(HAVE_UINT16_T) && SIZE_OF_UNSIGNED == 2 -typedef unsigned uint16_t; -#define HAVE_UINT16_T -#endif - -#if !defined(HAVE_UINT16_T) && SIZE_OF_UNSIGNED_SHORT == 2 -typedef unsigned short uint16_t; -#define HAVE_UINT16_T -#endif - -#if !defined(HAVE_UINT16_T) -#error No 16-bit unsigned integer type was found. -#endif - -/* - * Similarly for uint8_t - */ -#if !defined(HAVE_UINT8_T) -typedef unsigned char uint8_t; -#define HAVE_UINT8_T -#endif - -#if !defined(HAVE_UINT16_T) -#error No 8-bit unsigned integer type was found. -#endif - -/* Define intmax_t and uintmax_t if they are not already defined. */ -#if !defined(HAVE_INTMAX_T) -typedef int64_t intmax_t; -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#endif - -#if !defined(HAVE_UINTMAX_T) -typedef uint64_t uintmax_t; -#endif - - -#cmakedefine uintptr_t @uintptr_t@ - #cmakedefine WORDS_BIGENDIAN 1 #cmakedefine HAVE_BYTESWAP_H 1 @@ -219,7 +72,9 @@ typedef uint64_t uintmax_t; #define HAVE_MF_HC4 1 /* Define to 1 if you have the header file. */ -#cmakedefine HAVE_INTTYPES_H 1 +#ifdef KWIML_INT_HAVE_INTTYPES_H +# define HAVE_INTTYPES_H 1 +#endif /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIMITS_H 1 @@ -231,7 +86,9 @@ typedef uint64_t uintmax_t; #cmakedefine HAVE_STDBOOL_H 1 /* Define to 1 if you have the header file. */ -#cmakedefine HAVE_STDINT_H 1 +#ifdef KWIML_INT_HAVE_STDINT_H +# define HAVE_STDINT_H 1 +#endif /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRINGS_H 1 diff --git a/Utilities/cmlibrhash/CMakeLists.txt b/Utilities/cmlibrhash/CMakeLists.txt index 6067b7dcb9d..1b025fc7b63 100644 --- a/Utilities/cmlibrhash/CMakeLists.txt +++ b/Utilities/cmlibrhash/CMakeLists.txt @@ -37,4 +37,4 @@ include_directories( add_library(cmlibrhash ${librhash_sources}) -install(FILES COPYING README DESTINATION ${CMAKE_DOC_DIR}/cmlibrhash) +install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmlibrhash) diff --git a/Utilities/cmlibrhash/COPYING b/Utilities/cmlibrhash/COPYING index bf65ee1af98..be7d4a9fc73 100644 --- a/Utilities/cmlibrhash/COPYING +++ b/Utilities/cmlibrhash/COPYING @@ -1,15 +1,15 @@ - RHash License + BSD Zero Clause License -Copyright (c) 2005-2014 Aleksey Kravchenko +Copyright (c) 2005, Aleksey Kravchenko -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so. +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. -The Software is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. Use this program at your own risk! +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/Utilities/cmlibrhash/README b/Utilities/cmlibrhash/README deleted file mode 100644 index 4ea492fd2d3..00000000000 --- a/Utilities/cmlibrhash/README +++ /dev/null @@ -1,7 +0,0 @@ - === Notes on RHash License === - -The RHash program and LibRHash library are distributed under RHash License, -see the COPYING file for details. In particular, the program, the library -and source code can be used free of charge under the MIT, BSD, GPL, -commercial or freeware license without additional restrictions. In the case -the OSI-approved license is required the MIT license should be used. diff --git a/Utilities/cmlibrhash/librhash/algorithms.c b/Utilities/cmlibrhash/librhash/algorithms.c index fc0169058a6..cdd40533342 100644 --- a/Utilities/cmlibrhash/librhash/algorithms.c +++ b/Utilities/cmlibrhash/librhash/algorithms.c @@ -1,17 +1,17 @@ /* algorithms.c - the algorithms supported by the rhash library * - * Copyright: 2011-2012 Aleksey Kravchenko + * Copyright (c) 2011, Aleksey Kravchenko * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ #include @@ -27,7 +27,8 @@ #include "crc32.h" #include "ed2k.h" #include "edonr.h" -#include "gost.h" +#include "gost12.h" +#include "gost94.h" #include "has160.h" #include "md4.h" #endif @@ -54,18 +55,13 @@ #else # define NEED_OPENSSL_INIT 0 #endif /* USE_OPENSSL */ -#ifdef GENERATE_GOST_LOOKUP_TABLE -# define NEED_GOST_INIT (RHASH_GOST | RHASH_GOST_CRYPTOPRO) +#ifdef GENERATE_GOST94_LOOKUP_TABLE +# define NEED_GOST94_INIT (RHASH_GOST94 | RHASH_GOST94_CRYPTOPRO) #else -# define NEED_GOST_INIT 0 -#endif /* GENERATE_GOST_LOOKUP_TABLE */ -#ifdef GENERATE_CRC32_TABLE -# define NEED_CRC32_INIT RHASH_CRC32 -#else -# define NEED_CRC32_INIT 0 -#endif /* GENERATE_CRC32_TABLE */ +# define NEED_GOST94_INIT 0 +#endif /* GENERATE_GOST94_LOOKUP_TABLE */ -#define RHASH_NEED_INIT_ALG (NEED_CRC32_INIT | NEED_GOST_INIT | NEED_OPENSSL_INIT) +#define RHASH_NEED_INIT_ALG (NEED_GOST94_INIT | NEED_OPENSSL_INIT) unsigned rhash_uninitialized_algorithms = RHASH_NEED_INIT_ALG; rhash_hash_info* rhash_info_table = rhash_hash_info_default; @@ -75,10 +71,14 @@ int rhash_info_size = RHASH_HASH_COUNT; static void rhash_crc32_init(uint32_t* crc32); static void rhash_crc32_update(uint32_t* crc32, const unsigned char* msg, size_t size); static void rhash_crc32_final(uint32_t* crc32, unsigned char* result); +static void rhash_crc32c_init(uint32_t* crc32); +static void rhash_crc32c_update(uint32_t* crc32, const unsigned char* msg, size_t size); +static void rhash_crc32c_final(uint32_t* crc32, unsigned char* result); #endif #if 0 -rhash_info info_crc32 = { RHASH_CRC32, F_BE32, 4, "CRC32", "crc32" }; +rhash_info info_crc32 = { RHASH_CRC32, F_BE32, 4, "CRC32", "crc32" }; +rhash_info info_crc32c = { RHASH_CRC32C, F_BE32, 4, "CRC32C", "crc32c" }; rhash_info info_md4 = { RHASH_MD4, F_LE32, 16, "MD4", "md4" }; #endif rhash_info info_md5 = { RHASH_MD5, F_LE32, 16, "MD5", "md5" }; @@ -91,8 +91,10 @@ rhash_info info_ed2k = { RHASH_ED2K, F_LE32, 16, "ED2K", "ed2k" }; rhash_info info_aich = { RHASH_AICH, F_BS32, 20, "AICH", "aich" }; rhash_info info_whirlpool = { RHASH_WHIRLPOOL, F_BE64, 64, "WHIRLPOOL", "whirlpool" }; rhash_info info_rmd160 = { RHASH_RIPEMD160, F_LE32, 20, "RIPEMD-160", "ripemd160" }; -rhash_info info_gost = { RHASH_GOST, F_LE32, 32, "GOST", "gost" }; -rhash_info info_gostpro = { RHASH_GOST_CRYPTOPRO, F_LE32, 32, "GOST-CRYPTOPRO", "gost-cryptopro" }; +rhash_info info_gost12_256 = { RHASH_GOST12_256, F_LE64, 32, "GOST12-256", "gost12-256" }; +rhash_info info_gost12_512 = { RHASH_GOST12_512, F_LE64, 64, "GOST12-512", "gost12-512" }; +rhash_info info_gost94 = { RHASH_GOST94, F_LE32, 32, "GOST94", "gost94" }; +rhash_info info_gost94pro = { RHASH_GOST94_CRYPTOPRO, F_LE32, 32, "GOST94-CRYPTOPRO", "gost94-cryptopro" }; rhash_info info_has160 = { RHASH_HAS160, F_LE32, 20, "HAS-160", "has160" }; rhash_info info_snf128 = { RHASH_SNEFRU128, F_BE32, 16, "SNEFRU-128", "snefru128" }; rhash_info info_snf256 = { RHASH_SNEFRU256, F_BE32, 32, "SNEFRU-256", "snefru256" }; @@ -117,9 +119,10 @@ rhash_info info_sha3_512 = { RHASH_SHA3_512, F_LE64, 64, "SHA3-512", "sha3-512" #define upd(name) ((pupdate_t)(name##_update)) #define fin(name) ((pfinal_t)(name##_final)) #define iuf(name) ini(name), upd(name), fin(name) +#define iuf2(name1, name2) ini(name1), upd(name2), fin(name2) #define diuf(name) dgshft(name), ini(name), upd(name), fin(name) -/* information about all hashes */ +/* information about all supported hash functions */ rhash_hash_info rhash_hash_info_default[RHASH_HASH_COUNT] = { #if 0 @@ -135,28 +138,35 @@ rhash_hash_info rhash_hash_info_default[RHASH_HASH_COUNT] = { &info_aich, sizeof(aich_ctx), dgshft2(aich, sha1_context.hash), iuf(rhash_aich), (pcleanup_t)rhash_aich_cleanup }, /* 160 bit */ { &info_whirlpool, sizeof(whirlpool_ctx), dgshft(whirlpool), iuf(rhash_whirlpool), 0 }, /* 512 bit */ { &info_rmd160, sizeof(ripemd160_ctx), dgshft(ripemd160), iuf(rhash_ripemd160), 0 }, /* 160 bit */ - { &info_gost, sizeof(gost_ctx), dgshft(gost), iuf(rhash_gost), 0 }, /* 256 bit */ - { &info_gostpro, sizeof(gost_ctx), dgshft(gost), ini(rhash_gost_cryptopro), upd(rhash_gost), fin(rhash_gost), 0 }, /* 256 bit */ + { &info_gost94, sizeof(gost94_ctx), dgshft(gost94), iuf(rhash_gost94), 0 }, /* 256 bit */ + { &info_gost94pro, sizeof(gost94_ctx), dgshft(gost94), iuf2(rhash_gost94_cryptopro, rhash_gost94), 0 }, /* 256 bit */ { &info_has160, sizeof(has160_ctx), dgshft(has160), iuf(rhash_has160), 0 }, /* 160 bit */ - { &info_snf128, sizeof(snefru_ctx), dgshft(snefru), ini(rhash_snefru128), upd(rhash_snefru), fin(rhash_snefru), 0 }, /* 128 bit */ - { &info_snf256, sizeof(snefru_ctx), dgshft(snefru), ini(rhash_snefru256), upd(rhash_snefru), fin(rhash_snefru), 0 }, /* 256 bit */ + { &info_gost12_256, sizeof(gost12_ctx), dgshft2(gost12, h) + 32, iuf2(rhash_gost12_256, rhash_gost12), 0 }, /* 256 bit */ + { &info_gost12_512, sizeof(gost12_ctx), dgshft2(gost12, h), iuf2(rhash_gost12_512, rhash_gost12), 0 }, /* 512 bit */ #endif - { &info_sha224, sizeof(sha256_ctx), dgshft(sha256), ini(rhash_sha224), upd(rhash_sha256), fin(rhash_sha256), 0 }, /* 224 bit */ + { &info_sha224, sizeof(sha256_ctx), dgshft(sha256), iuf2(rhash_sha224, rhash_sha256), 0 }, /* 224 bit */ { &info_sha256, sizeof(sha256_ctx), dgshft(sha256), iuf(rhash_sha256), 0 }, /* 256 bit */ - { &info_sha384, sizeof(sha512_ctx), dgshft(sha512), ini(rhash_sha384), upd(rhash_sha512), fin(rhash_sha512), 0 }, /* 384 bit */ + { &info_sha384, sizeof(sha512_ctx), dgshft(sha512), iuf2(rhash_sha384, rhash_sha512), 0 }, /* 384 bit */ { &info_sha512, sizeof(sha512_ctx), dgshft(sha512), iuf(rhash_sha512), 0 }, /* 512 bit */ #if 0 - { &info_edr256, sizeof(edonr_ctx), dgshft2(edonr, u.data256.hash) + 32, iuf(rhash_edonr256), 0 }, /* 256 bit */ - { &info_edr512, sizeof(edonr_ctx), dgshft2(edonr, u.data512.hash) + 64, iuf(rhash_edonr512), 0 }, /* 512 bit */ + { &info_edr256, sizeof(edonr_ctx), dgshft2(edonr, u.data256.hash) + 32, iuf(rhash_edonr256), 0 }, /* 256 bit */ + { &info_edr512, sizeof(edonr_ctx), dgshft2(edonr, u.data512.hash) + 64, iuf(rhash_edonr512), 0 }, /* 512 bit */ +#endif + { &info_sha3_224, sizeof(sha3_ctx), dgshft(sha3), iuf2(rhash_sha3_224, rhash_sha3), 0 }, /* 224 bit */ + { &info_sha3_256, sizeof(sha3_ctx), dgshft(sha3), iuf2(rhash_sha3_256, rhash_sha3), 0 }, /* 256 bit */ + { &info_sha3_384, sizeof(sha3_ctx), dgshft(sha3), iuf2(rhash_sha3_384, rhash_sha3), 0 }, /* 384 bit */ + { &info_sha3_512, sizeof(sha3_ctx), dgshft(sha3), iuf2(rhash_sha3_512, rhash_sha3), 0 }, /* 512 bit */ +#if 0 + { &info_crc32c, sizeof(uint32_t), 0, iuf(rhash_crc32c), 0 }, /* 32 bit */ + { &info_snf128, sizeof(snefru_ctx), dgshft(snefru), iuf2(rhash_snefru128, rhash_snefru), 0 }, /* 128 bit */ + { &info_snf256, sizeof(snefru_ctx), dgshft(snefru), iuf2(rhash_snefru256, rhash_snefru), 0 }, /* 256 bit */ #endif - { &info_sha3_224, sizeof(sha3_ctx), dgshft(sha3), ini(rhash_sha3_224), upd(rhash_sha3), fin(rhash_sha3), 0 }, /* 224 bit */ - { &info_sha3_256, sizeof(sha3_ctx), dgshft(sha3), ini(rhash_sha3_256), upd(rhash_sha3), fin(rhash_sha3), 0 }, /* 256 bit */ - { &info_sha3_384, sizeof(sha3_ctx), dgshft(sha3), ini(rhash_sha3_384), upd(rhash_sha3), fin(rhash_sha3), 0 }, /* 384 bit */ - { &info_sha3_512, sizeof(sha3_ctx), dgshft(sha3), ini(rhash_sha3_512), upd(rhash_sha3), fin(rhash_sha3), 0 }, /* 512 bit */ }; /** * Initialize requested algorithms. + * + * @param mask ids of hash sums to initialize */ void rhash_init_algorithms(unsigned mask) { @@ -165,15 +175,26 @@ void rhash_init_algorithms(unsigned mask) /* verify that RHASH_HASH_COUNT is the index of the major bit of RHASH_ALL_HASHES */ assert(1 == (RHASH_ALL_HASHES >> (RHASH_HASH_COUNT - 1))); -#ifdef GENERATE_CRC32_TABLE - rhash_crc32_init_table(); -#endif -#ifdef GENERATE_GOST_LOOKUP_TABLE - rhash_gost_init_table(); +#ifdef GENERATE_GOST94_LOOKUP_TABLE + rhash_gost94_init_table(); #endif rhash_uninitialized_algorithms = 0; } +/** + * Returns information about a hash function by its hash_id. + * + * @param hash_id the id of hash algorithm + * @return pointer to the rhash_info structure containing the information + */ +const rhash_info* rhash_info_by_id(unsigned hash_id) +{ + hash_id &= RHASH_ALL_HASHES; + /* check that one and only one bit is set */ + if (!hash_id || (hash_id & (hash_id - 1)) != 0) return NULL; + return rhash_info_table[rhash_ctz(hash_id)].info; +} + #if 0 /* CRC32 helper functions */ @@ -217,4 +238,45 @@ static void rhash_crc32_final(uint32_t* crc32, unsigned char* result) result[2] = (unsigned char)(*crc32 >> 8), result[3] = (unsigned char)(*crc32); #endif } + +/** + * Initialize crc32c hash. + * + * @param crc32c pointer to the hash to initialize + */ +static void rhash_crc32c_init(uint32_t* crc32c) +{ + *crc32c = 0; /* note: context size is sizeof(uint32_t) */ +} + +/** + * Calculate message CRC32C hash. + * Can be called repeatedly with chunks of the message to be hashed. + * + * @param crc32c pointer to the hash + * @param msg message chunk + * @param size length of the message chunk + */ +static void rhash_crc32c_update(uint32_t* crc32c, const unsigned char* msg, size_t size) +{ + *crc32c = rhash_get_crc32c(*crc32c, msg, size); +} + +/** + * Store calculated hash into the given array. + * + * @param crc32c pointer to the current hash value + * @param result calculated hash in binary form + */ +static void rhash_crc32c_final(uint32_t* crc32c, unsigned char* result) +{ +#if defined(CPU_IA32) || defined(CPU_X64) + /* intel CPUs support assigment with non 32-bit aligned pointers */ + *(unsigned*)result = be2me_32(*crc32c); +#else + /* correct saving BigEndian integer on all archs */ + result[0] = (unsigned char)(*crc32c >> 24), result[1] = (unsigned char)(*crc32c >> 16); + result[2] = (unsigned char)(*crc32c >> 8), result[3] = (unsigned char)(*crc32c); +#endif +} #endif diff --git a/Utilities/cmlibrhash/librhash/algorithms.h b/Utilities/cmlibrhash/librhash/algorithms.h index 4db2517c918..01dda8868ed 100644 --- a/Utilities/cmlibrhash/librhash/algorithms.h +++ b/Utilities/cmlibrhash/librhash/algorithms.h @@ -2,9 +2,9 @@ #ifndef RHASH_ALGORITHMS_H #define RHASH_ALGORITHMS_H -#include /* for ptrdiff_t */ #include "rhash.h" #include "byte_order.h" +#include #ifdef __cplusplus extern "C" { @@ -15,8 +15,40 @@ extern "C" { # define RHASH_API #endif +/** + * Bit flag: default hash output format is base32. + */ +#define RHASH_INFO_BASE32 1 + +/** + * Information about a hash function. + */ +typedef struct rhash_info +{ + /** + * Hash function indentifier. + */ + unsigned hash_id; + /** + * Flags bit-mask, including RHASH_INFO_BASE32 bit. + */ + unsigned flags; + /** + The size of of the raw message digest in bytes. + */ + size_t digest_size; + /** + * The hash function name. + */ + const char* name; + /** + * The corresponding paramenter name in a magnet link. + */ + const char* magnet_name; +} rhash_info; + typedef void (*pinit_t)(void*); -typedef void (*pupdate_t)(void *ctx, const void* msg, size_t size); +typedef void (*pupdate_t)(void* ctx, const void* msg, size_t size); typedef void (*pfinal_t)(void*, unsigned char*); typedef void (*pcleanup_t)(void*); @@ -25,7 +57,7 @@ typedef void (*pcleanup_t)(void*); */ typedef struct rhash_hash_info { - rhash_info *info; + rhash_info* info; size_t context_size; ptrdiff_t digest_diff; pinit_t init; @@ -40,7 +72,7 @@ typedef struct rhash_hash_info typedef struct rhash_vector_item { struct rhash_hash_info* hash_info; - void *context; + void* context; } rhash_vector_item; /** @@ -52,8 +84,9 @@ typedef struct rhash_context_ext unsigned hash_vector_size; /* number of contained hash sums */ unsigned flags; unsigned state; - void *callback, *callback_data; - void *bt_ctx; + void* callback; + void* callback_data; + void* bt_ctx; rhash_vector_item vector[1]; /* contexts of contained hash sums */ } rhash_context_ext; @@ -63,6 +96,7 @@ extern int rhash_info_size; extern unsigned rhash_uninitialized_algorithms; extern rhash_info info_crc32; +extern rhash_info info_crc32c; extern rhash_info info_md4; extern rhash_info info_md5; extern rhash_info info_sha1; @@ -95,7 +129,7 @@ extern rhash_info info_edr512; #define F_SWAP64 4 /* define endianness flags */ -#ifndef CPU_BIG_ENDIAN +#if IS_LITTLE_ENDIAN #define F_LE32 0 #define F_LE64 0 #define F_BE32 F_SWAP32 @@ -108,6 +142,7 @@ extern rhash_info info_edr512; #endif void rhash_init_algorithms(unsigned mask); +const rhash_info* rhash_info_by_id(unsigned hash_id); /* get hash sum info by hash id */ #if defined(OPENSSL_RUNTIME) && !defined(USE_OPENSSL) # define USE_OPENSSL diff --git a/Utilities/cmlibrhash/librhash/byte_order.c b/Utilities/cmlibrhash/librhash/byte_order.c index 8ce6fc89e59..de2c583b59d 100644 --- a/Utilities/cmlibrhash/librhash/byte_order.c +++ b/Utilities/cmlibrhash/librhash/byte_order.c @@ -1,21 +1,21 @@ /* byte_order.c - byte order related platform dependent routines, * - * Copyright: 2008-2012 Aleksey Kravchenko + * Copyright (c) 2008, Aleksey Kravchenko * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ #include "byte_order.h" -#if !(__GNUC__ >= 4 || (__GNUC__ ==3 && __GNUC_MINOR__ >= 4)) /* if !GCC or GCC < 4.3 */ +#ifndef rhash_ctz # if _MSC_VER >= 1300 && (_M_IX86 || _M_AMD64 || _M_IA64) /* if MSVC++ >= 2002 on x86/x64 */ # include @@ -59,7 +59,7 @@ unsigned rhash_ctz(unsigned x) return (unsigned)bit_pos[((uint32_t)((x & -x) * 0x077CB531U)) >> 27]; } # endif /* _MSC_VER >= 1300... */ -#endif /* !(GCC >= 4.3) */ +#endif /* rhash_ctz */ /** * Copy a memory block with simultaneous exchanging byte order. @@ -79,10 +79,12 @@ void rhash_swap_copy_str_to_u32(void* to, int index, const void* from, size_t le const uint32_t* src = (const uint32_t*)from; const uint32_t* end = (const uint32_t*)((const char*)src + length); uint32_t* dst = (uint32_t*)((char*)to + index); - while (src < end) *(dst++) = bswap_32( *(src++) ); + for (; src < end; dst++, src++) + *dst = bswap_32(*src); } else { const char* src = (const char*)from; - for (length += index; (size_t)index < length; index++) ((char*)to)[index ^ 3] = *(src++); + for (length += index; (size_t)index < length; index++) + ((char*)to)[index ^ 3] = *(src++); } } @@ -141,10 +143,31 @@ void rhash_swap_copy_u64_to_str(void* to, const void* from, size_t length) * @param arr the array to process * @param length array length */ -void rhash_u32_mem_swap(unsigned *arr, int length) +void rhash_u32_mem_swap(unsigned* arr, int length) { unsigned* end = arr + length; for (; arr < end; arr++) { *arr = bswap_32(*arr); } } + +#ifdef HAS_INTEL_CPUID +#include + +static uint64_t get_cpuid_features(void) +{ + uint32_t tmp, edx, ecx; + if (__get_cpuid(1, &tmp, &tmp, &ecx, &edx)) + return ((((uint64_t)ecx) << 32) ^ edx); + return 0; +} + +int has_cpu_feature(unsigned feature_bit) +{ + static uint64_t features; + const uint64_t feature = ((uint64_t)1) << feature_bit; + if (!features) + features = (get_cpuid_features() | 1); + return !!(features & feature); +} +#endif diff --git a/Utilities/cmlibrhash/librhash/byte_order.h b/Utilities/cmlibrhash/librhash/byte_order.h index d34a020b860..cfb9e25e35f 100644 --- a/Utilities/cmlibrhash/librhash/byte_order.h +++ b/Utilities/cmlibrhash/librhash/byte_order.h @@ -4,6 +4,19 @@ #include "ustd.h" #include +#if 0 +#if defined(__GLIBC__) +# include +#endif +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) +# include +#elif defined (__NetBSD__) || defined(__OpenBSD__) +# include +#endif + + #ifdef __cplusplus extern "C" { #endif @@ -26,9 +39,7 @@ extern "C" { # endif #endif - -/* detect CPU endianness */ -#include +#include #if KWIML_ABI_ENDIAN_ID == KWIML_ABI_ENDIAN_ID_LITTLE # define CPU_LITTLE_ENDIAN # define IS_BIG_ENDIAN 0 @@ -37,8 +48,53 @@ extern "C" { # define CPU_BIG_ENDIAN # define IS_BIG_ENDIAN 1 # define IS_LITTLE_ENDIAN 0 +#endif + +#if 0 +#define RHASH_BYTE_ORDER_LE 1234 +#define RHASH_BYTE_ORDER_BE 4321 + +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +# define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_LE +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_BE +#elif defined(_BYTE_ORDER) +# if defined(_LITTLE_ENDIAN) && (_BYTE_ORDER == _LITTLE_ENDIAN) +# define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_LE +# elif defined(_BIG_ENDIAN) && (_BYTE_ORDER == _BIG_ENDIAN) +# define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_BE +# endif +#elif defined(__sun) && defined(_LITTLE_ENDIAN) +# define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_LE +#elif defined(__sun) && defined(_BIG_ENDIAN) +# define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_BE +#endif + +/* try detecting endianness by CPU */ +#ifdef RHASH_BYTE_ORDER +#elif defined(CPU_IA32) || defined(CPU_X64) || defined(__ia64) || defined(__ia64__) || \ + defined(__alpha__) || defined(_M_ALPHA) || defined(vax) || defined(MIPSEL) || \ + defined(_ARM_) || defined(__arm__) +# define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_LE +#elif defined(__sparc) || defined(__sparc__) || defined(sparc) || \ + defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_POWER) || \ + defined(__POWERPC__) || defined(POWERPC) || defined(__powerpc) || \ + defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || \ + defined(__hpux) || defined(_MIPSEB) || defined(mc68000) || \ + defined(__s390__) || defined(__s390x__) || defined(sel) +# define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_BE #else -# error "Can't detect CPU architechture" +# error "Can't detect CPU architechture" +#endif + +#define IS_BIG_ENDIAN (RHASH_BYTE_ORDER == RHASH_BYTE_ORDER_BE) +#define IS_LITTLE_ENDIAN (RHASH_BYTE_ORDER == RHASH_BYTE_ORDER_LE) +#endif + +#ifndef __has_builtin +# define __has_builtin(x) 0 #endif #define IS_ALIGNED_32(p) (0 == (3 & ((const char*)(p) - (const char*)0))) @@ -56,11 +112,23 @@ extern "C" { #if defined(_MSC_VER) || defined(__BORLANDC__) #define I64(x) x##ui64 #else -#define I64(x) x##LL +#define I64(x) x##ULL #endif -/* convert a hash flag to index */ -#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) /* GCC < 3.4 */ +#if defined(_MSC_VER) +#define RHASH_INLINE __inline +#elif defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define RHASH_INLINE inline +#elif defined(__GNUC__) +#define RHASH_INLINE __inline__ +#else +#define RHASH_INLINE +#endif + +/* define rhash_ctz - count traling zero bits */ +#if (defined(__GNUC__) && __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) || \ + (defined(__clang__) && __has_builtin(__builtin_ctz)) +/* GCC >= 3.4 or clang */ # define rhash_ctz(x) __builtin_ctz(x) #else unsigned rhash_ctz(unsigned); /* define as function */ @@ -69,42 +137,31 @@ unsigned rhash_ctz(unsigned); /* define as function */ void rhash_swap_copy_str_to_u32(void* to, int index, const void* from, size_t length); void rhash_swap_copy_str_to_u64(void* to, int index, const void* from, size_t length); void rhash_swap_copy_u64_to_str(void* to, const void* from, size_t length); -void rhash_u32_mem_swap(unsigned *p, int length_in_u32); +void rhash_u32_mem_swap(unsigned* p, int length_in_u32); -#ifndef __has_builtin -# define __has_builtin(x) 0 -#endif - -/* define bswap_32 */ -#if defined(__GNUC__) && defined(CPU_IA32) && !defined(__i386__) -/* for intel x86 CPU */ -static inline uint32_t bswap_32(uint32_t x) { - __asm("bswap\t%0" : "=r" (x) : "0" (x)); - return x; -} -#elif defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC__ > 4 || __GNUC_MINOR__ >= 3) -/* for GCC >= 4.3 */ -# define bswap_32(x) __builtin_bswap32(x) -#elif defined(__clang__) && __has_builtin(__builtin_bswap32) +/* bswap definitions */ +#if (defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC__ > 4 || __GNUC_MINOR__ >= 3)) || \ + (defined(__clang__) && __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64)) +/* GCC >= 4.3 or clang */ # define bswap_32(x) __builtin_bswap32(x) +# define bswap_64(x) __builtin_bswap64(x) #elif (_MSC_VER > 1300) && (defined(CPU_IA32) || defined(CPU_X64)) /* MS VC */ # define bswap_32(x) _byteswap_ulong((unsigned long)x) +# define bswap_64(x) _byteswap_uint64((__int64)x) #else -/* general bswap_32 definition */ -static uint32_t bswap_32(uint32_t x) { - x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0x00FF00FF); +/* fallback to generic bswap definition */ +static RHASH_INLINE uint32_t bswap_32(uint32_t x) +{ +# if defined(__GNUC__) && defined(CPU_IA32) && !defined(__i386__) && !defined(RHASH_NO_ASM) + __asm("bswap\t%0" : "=r" (x) : "0" (x)); /* gcc x86 version */ + return x; +# else + x = ((x << 8) & 0xFF00FF00u) | ((x >> 8) & 0x00FF00FFu); return (x >> 16) | (x << 16); +# endif } -#endif /* bswap_32 */ - -#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC__ > 4 || __GNUC_MINOR__ >= 3) -# define bswap_64(x) __builtin_bswap64(x) -#elif defined(__clang__) && __has_builtin(__builtin_bswap64) -# define bswap_64(x) __builtin_bswap64(x) -#elif (_MSC_VER > 1300) && (defined(CPU_IA32) || defined(CPU_X64)) /* MS VC */ -# define bswap_64(x) _byteswap_uint64((__int64)x) -#else -static uint64_t bswap_64(uint64_t x) { +static RHASH_INLINE uint64_t bswap_64(uint64_t x) +{ union { uint64_t ll; uint32_t l[2]; @@ -114,9 +171,9 @@ static uint64_t bswap_64(uint64_t x) { r.l[1] = bswap_32(w.l[0]); return r.ll; } -#endif +#endif /* bswap definitions */ -#ifdef CPU_BIG_ENDIAN +#if IS_BIG_ENDIAN # define be2me_32(x) (x) # define be2me_64(x) (x) # define le2me_32(x) bswap_32(x) @@ -129,7 +186,7 @@ static uint64_t bswap_64(uint64_t x) { # define me64_to_be_str(to, from, length) memcpy((to), (from), (length)) # define me64_to_le_str(to, from, length) rhash_swap_copy_u64_to_str((to), (from), (length)) -#else /* CPU_BIG_ENDIAN */ +#else /* IS_BIG_ENDIAN */ # define be2me_32(x) bswap_32(x) # define be2me_64(x) bswap_64(x) # define le2me_32(x) (x) @@ -141,7 +198,7 @@ static uint64_t bswap_64(uint64_t x) { # define le64_copy(to, index, from, length) memcpy((to) + (index), (from), (length)) # define me64_to_be_str(to, from, length) rhash_swap_copy_u64_to_str((to), (from), (length)) # define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) -#endif /* CPU_BIG_ENDIAN */ +#endif /* IS_BIG_ENDIAN */ /* ROTL/ROTR macros rotate a 32/64-bit word left/right by n bits */ #define ROTL32(dword, n) ((dword) << (n) ^ ((dword) >> (32 - (n)))) @@ -149,6 +206,16 @@ static uint64_t bswap_64(uint64_t x) { #define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) #define ROTR64(qword, n) ((qword) >> (n) ^ ((qword) << (64 - (n)))) +#define CPU_FEATURE_SSE4_2 (52) + +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) \ + && (defined(CPU_X64) || defined(CPU_IA32)) +# define HAS_INTEL_CPUID +int has_cpu_feature(unsigned feature_bit); +#else +# define has_cpu_feature(x) (0) +#endif + #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ diff --git a/Utilities/cmlibrhash/librhash/hex.c b/Utilities/cmlibrhash/librhash/hex.c index c941149ab35..f0bbf043ed5 100644 --- a/Utilities/cmlibrhash/librhash/hex.c +++ b/Utilities/cmlibrhash/librhash/hex.c @@ -1,71 +1,57 @@ /* hex.c - conversion for hexadecimal and base32 strings. * - * Copyright: 2008-2012 Aleksey Kravchenko + * Copyright (c) 2008, Aleksey Kravchenko * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ -#include -#include #include "hex.h" - -/** -* Convert a byte to a hexadecimal number. The result, consisting of two -* hexadecimal digits is stored into a buffer. - * - * @param dest the buffer to receive two symbols of hex representation - * @param byte the byte to decode - * @param upper_case flag to print string in uppercase - * @return pointer to the chararcter just after the written number (dest + 2) - */ -char* rhash_print_hex_byte(char *dest, const unsigned char byte, int upper_case) -{ - const char add = (upper_case ? 'A' - 10 : 'a' - 10); - unsigned char c = (byte >> 4) & 15; - *dest++ = (c > 9 ? c + add : c + '0'); - c = byte & 15; - *dest++ = (c > 9 ? c + add : c + '0'); - return dest; -} +#include +#include +#include /** * Store hexadecimal representation of a binary string to given buffer. * - * @param dest the buffer to receive hexadecimal representation + * @param dst the buffer to receive hexadecimal representation * @param src binary string - * @param len string length + * @param length string length * @param upper_case flag to print string in uppercase */ -void rhash_byte_to_hex(char *dest, const unsigned char *src, unsigned len, int upper_case) +void rhash_byte_to_hex(char* dst, const unsigned char* src, size_t length, int upper_case) { - while (len-- > 0) { - dest = rhash_print_hex_byte(dest, *src++, upper_case); + const char hex_add = (upper_case ? 'A' - 10 : 'a' - 10); + for (; length > 0; src++, length--) { + const unsigned char hi = (*src >> 4) & 15; + const unsigned char lo = *src & 15; + *dst++ = (hi > 9 ? hi + hex_add : hi + '0'); + *dst++ = (lo > 9 ? lo + hex_add : lo + '0'); } - *dest = '\0'; + *dst = '\0'; } /** * Encode a binary string to base32. * - * @param dest the buffer to store result + * @param dst the buffer to store result * @param src binary string - * @param len string length + * @param length string length * @param upper_case flag to print string in uppercase */ -void rhash_byte_to_base32(char* dest, const unsigned char* src, unsigned len, int upper_case) +void rhash_byte_to_base32(char* dst, const unsigned char* src, size_t length, int upper_case) { const char a = (upper_case ? 'A' : 'a'); unsigned shift = 0; unsigned char word; - const unsigned char* e = src + len; + const unsigned char* e = src + length; while (src < e) { if (shift > 3) { word = (*src & (0xFF >> shift)); @@ -79,25 +65,25 @@ void rhash_byte_to_base32(char* dest, const unsigned char* src, unsigned len, in word = ( *src >> ( (8 - shift) & 7 ) ) & 0x1F; if (shift == 0) src++; } - *dest++ = ( word < 26 ? word + a : word + '2' - 26 ); + *dst++ = ( word < 26 ? word + a : word + '2' - 26 ); } - *dest = '\0'; + *dst = '\0'; } /** * Encode a binary string to base64. * Encoded output length is always a multiple of 4 bytes. * - * @param dest the buffer to store result + * @param dst the buffer to store result * @param src binary string - * @param len string length + * @param length string length */ -void rhash_byte_to_base64(char* dest, const unsigned char* src, unsigned len) +void rhash_byte_to_base64(char* dst, const unsigned char* src, size_t length) { static const char* tail = "0123456789+/"; unsigned shift = 0; unsigned char word; - const unsigned char* e = src + len; + const unsigned char* e = src + length; while (src < e) { if (shift > 2) { word = (*src & (0xFF >> shift)); @@ -111,45 +97,80 @@ void rhash_byte_to_base64(char* dest, const unsigned char* src, unsigned len) word = ( *src >> ( (8 - shift) & 7 ) ) & 0x3F; if (shift == 0) src++; } - *dest++ = ( word < 52 ? (word < 26 ? word + 'A' : word - 26 + 'a') : tail[word - 52]); + *dst++ = ( word < 52 ? (word < 26 ? word + 'A' : word - 26 + 'a') : tail[word - 52]); } if (shift > 0) { - *dest++ = '='; - if (shift == 4) *dest++ = '='; + *dst++ = '='; + if (shift == 4) *dst++ = '='; } - *dest = '\0'; + *dst = '\0'; } -/* unsafe characters are "<>{}[]%#/|\^~`@:;?=&+ */ -#define IS_GOOD_URL_CHAR(c) (isalnum((unsigned char)c) || strchr("$-_.!'(),", c)) +size_t rhash_base64_url_encoded_helper(char* dst, const unsigned char* src, size_t length, int url_encode, int upper_case) +{ +#define B64_CHUNK_SIZE 120 + char buffer[164]; + assert((BASE64_LENGTH(B64_CHUNK_SIZE) + 4) <= sizeof(buffer)); + assert((B64_CHUNK_SIZE % 6) == 0); + if (url_encode) { + size_t result_length = 0; + for (; length > 0; src += B64_CHUNK_SIZE) { + size_t chunk_size = (length < B64_CHUNK_SIZE ? length : B64_CHUNK_SIZE); + size_t encoded_length; + rhash_byte_to_base64(buffer, src, chunk_size); + encoded_length = rhash_urlencode(dst, buffer, BASE64_LENGTH(chunk_size), upper_case); + result_length += encoded_length; + dst += encoded_length; + length -= chunk_size; + } + return result_length; + } + rhash_byte_to_base64(dst, src, length); + return BASE64_LENGTH(length); +} + +/* RFC 3986: safe url characters are ascii alpha-numeric and "-._~", other characters should be percent-encoded */ +static unsigned url_safe_char_mask[4] = { 0, 0x03ff6000, 0x87fffffe, 0x47fffffe }; +#define IS_URL_GOOD_CHAR(c) ((unsigned)(c) < 128 && (url_safe_char_mask[c >> 5] & (1 << (c & 31)))) /** - * URL-encode a string. + * URL-encode specified binary string. * - * @param dst buffer to receive result or NULL to calculate - * the lengths of encoded string - * @param filename the file name + * @param dst (nullable) buffer to output encoded string to, + * NULL to just calculate the lengths of encoded string + * @param src binary string to encode + * @param size size of the binary string + * @param upper_case flag to output hex-codes in uppercase * @return the length of the result string */ -int rhash_urlencode(char *dst, const char *name) +size_t rhash_urlencode(char* dst, const char* src, size_t size, int upper_case) { - const char *start; + const char* start; + size_t i; if (!dst) { - int len; - for (len = 0; *name; name++) len += (IS_GOOD_URL_CHAR(*name) ? 1 : 3); - return len; - } - /* encode URL as specified by RFC 1738 */ - for (start = dst; *name; name++) { - if ( IS_GOOD_URL_CHAR(*name) ) { - *dst++ = *name; - } else { - *dst++ = '%'; - dst = rhash_print_hex_byte(dst, *name, 'A'); + size_t length = size; + for (i = 0; i < size; i++) + if (!IS_URL_GOOD_CHAR(src[i])) + length += 2; + return length; + } else { + const char hex_add = (upper_case ? 'A' - 10 : 'a' - 10); + start = dst; + /* percent-encode all but unreserved URL characters */ + for (i = 0; i < size; i++) { + if (IS_URL_GOOD_CHAR(src[i])) { + *dst++ = src[i]; + } else { + unsigned char hi = ((unsigned char)(src[i]) >> 4) & 0x0f; + unsigned char lo = (unsigned char)(src[i]) & 0x0f; + *dst++ = '%'; + *dst++ = (hi > 9 ? hi + hex_add : hi + '0'); + *dst++ = (lo > 9 ? lo + hex_add : lo + '0'); + } } + *dst = 0; } - *dst = 0; - return (int)(dst - start); + return dst - start; } /** @@ -160,10 +181,11 @@ int rhash_urlencode(char *dst, const char *name) * @param number the number to print * @return length of the printed number (without trailing '\0') */ -int rhash_sprintI64(char *dst, uint64_t number) +int rhash_sprintI64(char* dst, uint64_t number) { /* The biggest number has 20 digits: 2^64 = 18 446 744 073 709 551 616 */ - char buf[24], *p; + char buf[24]; + char* p; size_t length; if (dst == NULL) { diff --git a/Utilities/cmlibrhash/librhash/hex.h b/Utilities/cmlibrhash/librhash/hex.h index 2b365e2fc10..6bea0369288 100644 --- a/Utilities/cmlibrhash/librhash/hex.h +++ b/Utilities/cmlibrhash/librhash/hex.h @@ -8,12 +8,13 @@ extern "C" { #endif -void rhash_byte_to_hex(char *dest, const unsigned char *src, unsigned len, int upper_case); -void rhash_byte_to_base32(char* dest, const unsigned char* src, unsigned len, int upper_case); -void rhash_byte_to_base64(char* dest, const unsigned char* src, unsigned len); -char* rhash_print_hex_byte(char *dest, const unsigned char byte, int upper_case); -int rhash_urlencode(char *dst, const char *name); -int rhash_sprintI64(char *dst, uint64_t number); +void rhash_byte_to_hex(char* dest, const unsigned char* src, size_t length, int upper_case); +void rhash_byte_to_base32(char* dest, const unsigned char* src, size_t length, int upper_case); +void rhash_byte_to_base64(char* dest, const unsigned char* src, size_t length); +char* rhash_print_hex_byte(char* dest, const unsigned char byte, int upper_case); +size_t rhash_urlencode(char* dst, const char* str, size_t size, int upper_case); +size_t rhash_base64_url_encoded_helper(char* dst, const unsigned char* src, size_t length, int url_encode, int upper_case); +int rhash_sprintI64(char* dst, uint64_t number); #define BASE32_LENGTH(bytes) (((bytes) * 8 + 4) / 5) #define BASE64_LENGTH(bytes) ((((bytes) + 2) / 3) * 4) diff --git a/Utilities/cmlibrhash/librhash/md5.c b/Utilities/cmlibrhash/librhash/md5.c index b20de45e9d8..9b768220f8a 100644 --- a/Utilities/cmlibrhash/librhash/md5.c +++ b/Utilities/cmlibrhash/librhash/md5.c @@ -1,17 +1,17 @@ /* md5.c - an implementation of the MD5 algorithm, based on RFC 1321. * - * Copyright: 2007-2012 Aleksey Kravchenko + * Copyright (c) 2007, Aleksey Kravchenko * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ #include @@ -23,7 +23,7 @@ * * @param ctx context to initialize */ -void rhash_md5_init(md5_ctx *ctx) +void rhash_md5_init(md5_ctx* ctx) { ctx->length = 0; @@ -162,7 +162,7 @@ static void rhash_md5_process_block(unsigned state[4], const unsigned* x) * @param msg message chunk * @param size length of the message chunk */ -void rhash_md5_update(md5_ctx *ctx, const unsigned char* msg, size_t size) +void rhash_md5_update(md5_ctx* ctx, const unsigned char* msg, size_t size) { unsigned index = (unsigned)ctx->length & 63; ctx->length += size; @@ -205,7 +205,7 @@ void rhash_md5_update(md5_ctx *ctx, const unsigned char* msg, size_t size) * @param ctx the algorithm context containing current hashing state * @param result calculated hash in binary form */ -void rhash_md5_final(md5_ctx *ctx, unsigned char* result) +void rhash_md5_final(md5_ctx* ctx, unsigned char* result) { unsigned index = ((unsigned)ctx->length & 63) >> 2; unsigned shift = ((unsigned)ctx->length & 3) * 8; diff --git a/Utilities/cmlibrhash/librhash/md5.h b/Utilities/cmlibrhash/librhash/md5.h index 1af6f133c92..12a6b527867 100644 --- a/Utilities/cmlibrhash/librhash/md5.h +++ b/Utilities/cmlibrhash/librhash/md5.h @@ -20,9 +20,9 @@ typedef struct md5_ctx /* hash functions */ -void rhash_md5_init(md5_ctx *ctx); -void rhash_md5_update(md5_ctx *ctx, const unsigned char* msg, size_t size); -void rhash_md5_final(md5_ctx *ctx, unsigned char result[16]); +void rhash_md5_init(md5_ctx* ctx); +void rhash_md5_update(md5_ctx* ctx, const unsigned char* msg, size_t size); +void rhash_md5_final(md5_ctx* ctx, unsigned char result[16]); #ifdef __cplusplus } /* extern "C" */ diff --git a/Utilities/cmlibrhash/librhash/rhash.c b/Utilities/cmlibrhash/librhash/rhash.c index 34e1eb30582..ce6ace41d42 100644 --- a/Utilities/cmlibrhash/librhash/rhash.c +++ b/Utilities/cmlibrhash/librhash/rhash.c @@ -1,42 +1,40 @@ /* rhash.c - implementation of LibRHash library calls * - * Copyright: 2008-2012 Aleksey Kravchenko + * Copyright (c) 2008, Aleksey Kravchenko * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ +/* modifier for Windows DLL */ +#if (defined(_WIN32) || defined(__CYGWIN__)) && defined(RHASH_EXPORTS) +# define RHASH_API __declspec(dllexport) +#endif + /* macros for large file support, must be defined before any include file */ #define _LARGEFILE64_SOURCE #define _FILE_OFFSET_BITS 64 #include "ustd.h" /* Need this first within CMake. */ -#include /* memset() */ -#include /* free() */ -#include /* ptrdiff_t */ -#include -#include -#include - -/* modifier for Windows DLL */ -#if defined(_WIN32) && defined(RHASH_EXPORTS) -# define RHASH_API __declspec(dllexport) -#endif - -#include "byte_order.h" +#include "rhash.h" #include "algorithms.h" -#include "util.h" +#include "byte_order.h" #include "hex.h" -#include "rhash.h" /* RHash library interface */ +#include "util.h" +#include +#include +#include +#include +#include #define STATE_ACTIVE 0xb01dbabe #define STATE_STOPED 0xdeadbeef @@ -45,11 +43,8 @@ #define RCTX_FINALIZED 0x2 #define RCTX_FINALIZED_MASK (RCTX_AUTO_FINAL | RCTX_FINALIZED) #define RHPR_FORMAT (RHPR_RAW | RHPR_HEX | RHPR_BASE32 | RHPR_BASE64) -#define RHPR_MODIFIER (RHPR_UPPERCASE | RHPR_REVERSE) +#define RHPR_MODIFIER (RHPR_UPPERCASE | RHPR_URLENCODE | RHPR_REVERSE) -/** - * Initialize static data of rhash algorithms - */ void rhash_library_init(void) { rhash_init_algorithms(RHASH_ALL_HASHES); @@ -58,31 +53,18 @@ void rhash_library_init(void) #endif } -/** - * Returns the number of supported hash algorithms. - * - * @return the number of supported hash functions - */ int RHASH_API rhash_count(void) { return rhash_info_size; } -/* Lo-level rhash library functions */ +/* LOW-LEVEL LIBRHASH INTERFACE */ -/** - * Allocate and initialize RHash context for calculating hash(es). - * After initializing rhash_update()/rhash_final() functions should be used. - * Then the context must be freed by calling rhash_free(). - * - * @param hash_id union of bit flags, containing ids of hashes to calculate. - * @return initialized rhash context, NULL on error and errno is set - */ RHASH_API rhash rhash_init(unsigned hash_id) { unsigned tail_bit_index; /* index of hash_id trailing bit */ unsigned num = 0; /* number of hashes to compute */ - rhash_context_ext *rctx = NULL; /* allocated rhash context */ + rhash_context_ext* rctx = NULL; /* allocated rhash context */ size_t hash_size_sum = 0; /* size of hash contexts to store in rctx */ unsigned i, bit_index, id; @@ -123,7 +105,7 @@ RHASH_API rhash rhash_init(unsigned hash_id) } /* align the size of the rhash context common part */ - aligned_size = (offsetof(rhash_context_ext, vector[num]) + 7) & ~7; + aligned_size = ((offsetof(rhash_context_ext, vector) + sizeof(rhash_vector_item) * num) + 7) & ~7; assert(aligned_size >= sizeof(rhash_context_ext)); /* allocate rhash context with enough memory to store contexts of all used hashes */ @@ -170,11 +152,6 @@ RHASH_API rhash rhash_init(unsigned hash_id) return &rctx->rc; /* return allocated and initialized rhash context */ } -/** - * Free RHash context memory. - * - * @param ctx the context to free. - */ void rhash_free(rhash ctx) { rhash_context_ext* const ectx = (rhash_context_ext*)ctx; @@ -195,12 +172,6 @@ void rhash_free(rhash ctx) free(ectx); } -/** - * Re-initialize RHash context to reuse it. - * Useful to speed up processing of many small messages. - * - * @param ctx context to reinitialize - */ RHASH_API void rhash_reset(rhash ctx) { rhash_context_ext* const ectx = (rhash_context_ext*)ctx; @@ -223,15 +194,6 @@ RHASH_API void rhash_reset(rhash ctx) ectx->flags &= ~RCTX_FINALIZED; /* clear finalized state */ } -/** - * Calculate hashes of message. - * Can be called repeatedly with chunks of the message to be hashed. - * - * @param ctx the rhash context - * @param message message chunk - * @param length length of the message chunk - * @return 0 on success; On fail return -1 and set errno - */ RHASH_API int rhash_update(rhash ctx, const void* message, size_t length) { rhash_context_ext* const ectx = (rhash_context_ext*)ctx; @@ -251,13 +213,6 @@ RHASH_API int rhash_update(rhash ctx, const void* message, size_t length) return 0; /* no error processing at the moment */ } -/** - * Finalize hash calculation and optionally store the first hash. - * - * @param ctx the rhash context - * @param first_result optional buffer to store a calculated hash with the lowest available id - * @return 0 on success; On fail return -1 and set errno - */ RHASH_API int rhash_final(rhash ctx, unsigned char* first_result) { unsigned i = 0; @@ -295,7 +250,7 @@ static void rhash_put_digest(rhash ctx, unsigned hash_id, unsigned char* result) { rhash_context_ext* const ectx = (rhash_context_ext*)ctx; unsigned i; - rhash_vector_item *item; + rhash_vector_item* item; struct rhash_hash_info* info; unsigned char* digest; @@ -332,34 +287,14 @@ static void rhash_put_digest(rhash ctx, unsigned hash_id, unsigned char* result) } } -/** - * Set the callback function to be called from the - * rhash_file() and rhash_file_update() functions - * on processing every file block. The file block - * size is set internally by rhash and now is 8 KiB. - * - * @param ctx rhash context - * @param callback pointer to the callback function - * @param callback_data pointer to data passed to the callback - */ RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data) { - ((rhash_context_ext*)ctx)->callback = callback; + ((rhash_context_ext*)ctx)->callback = (void*)callback; ((rhash_context_ext*)ctx)->callback_data = callback_data; } +/* HIGH-LEVEL LIBRHASH INTERFACE */ -/* hi-level message hashing interface */ - -/** - * Compute a hash of the given message. - * - * @param hash_id id of hash sum to compute - * @param message the message to process - * @param length message length - * @param result buffer to receive binary hash string - * @return 0 on success, -1 on error - */ RHASH_API int rhash_msg(unsigned hash_id, const void* message, size_t length, unsigned char* result) { rhash ctx; @@ -372,22 +307,12 @@ RHASH_API int rhash_msg(unsigned hash_id, const void* message, size_t length, un return 0; } -/** - * Hash a file or stream. Multiple hashes can be computed. - * First, inintialize ctx parameter with rhash_init() before calling - * rhash_file_update(). Then use rhash_final() and rhash_print() - * to retrive hash values. Finaly call rhash_free() on ctx - * to free allocated memory or call rhash_reset() to reuse ctx. - * - * @param ctx rhash context - * @param fd descriptor of the file to hash - * @return 0 on success, -1 on error and errno is set - */ RHASH_API int rhash_file_update(rhash ctx, FILE* fd) { rhash_context_ext* const ectx = (rhash_context_ext*)ctx; const size_t block_size = 8192; - unsigned char *buffer, *pmem; + unsigned char* buffer; + unsigned char* pmem; size_t length = 0, align8; int res = 0; if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */ @@ -425,14 +350,6 @@ RHASH_API int rhash_file_update(rhash ctx, FILE* fd) return res; } -/** - * Compute a single hash for given file. - * - * @param hash_id id of hash sum to compute - * @param filepath path to the file to hash - * @param result buffer to receive hash value with the lowest requested id - * @return 0 on success, -1 on error and errno is set - */ RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result) { FILE* fd; @@ -447,7 +364,10 @@ RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* if ((fd = fopen(filepath, "rb")) == NULL) return -1; - if ((ctx = rhash_init(hash_id)) == NULL) return -1; + if ((ctx = rhash_init(hash_id)) == NULL) { + fclose(fd); + return -1; + } res = rhash_file_update(ctx, fd); /* hash the file */ fclose(fd); @@ -460,14 +380,6 @@ RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* #ifdef _WIN32 /* windows only function */ #include -/** - * Compute a single hash for given file. - * - * @param hash_id id of hash sum to compute - * @param filepath path to the file to hash - * @param result buffer to receive hash value with the lowest requested id - * @return 0 on success, -1 on error, -1 on error and errno is set - */ RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned char* result) { FILE* fd; @@ -482,7 +394,10 @@ RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned ch if ((fd = _wfsopen(filepath, L"rb", _SH_DENYWR)) == NULL) return -1; - if ((ctx = rhash_init(hash_id)) == NULL) return -1; + if ((ctx = rhash_init(hash_id)) == NULL) { + fclose(fd); + return -1; + } res = rhash_file_update(ctx, fd); /* hash the file */ fclose(fd); @@ -495,28 +410,7 @@ RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned ch /* RHash information functions */ -/** - * Returns information about a hash function by its hash_id. - * - * @param hash_id the id of hash algorithm - * @return pointer to the rhash_info structure containing the information - */ -const rhash_info* rhash_info_by_id(unsigned hash_id) -{ - hash_id &= RHASH_ALL_HASHES; - /* check that only one bit is set */ - if (hash_id != (hash_id & -(int)hash_id)) return NULL; - /* note: alternative condition is (hash_id == 0 || (hash_id & (hash_id - 1)) != 0) */ - return rhash_info_table[rhash_ctz(hash_id)].info; -} - #if 0 -/** - * Detect default digest output format for given hash algorithm. - * - * @param hash_id the id of hash algorithm - * @return 1 for base32 format, 0 for hexadecimal - */ RHASH_API int rhash_is_base32(unsigned hash_id) { /* fast method is just to test a bit-mask */ @@ -524,12 +418,6 @@ RHASH_API int rhash_is_base32(unsigned hash_id) } #endif -/** - * Returns size of binary digest for given hash algorithm. - * - * @param hash_id the id of hash algorithm - * @return digest size in bytes - */ RHASH_API int rhash_get_digest_size(unsigned hash_id) { hash_id &= RHASH_ALL_HASHES; @@ -537,12 +425,6 @@ RHASH_API int rhash_get_digest_size(unsigned hash_id) return (int)rhash_info_table[rhash_ctz(hash_id)].info->digest_size; } -/** - * Returns length of digest hash string in default output format. - * - * @param hash_id the id of hash algorithm - * @return the length of hash string - */ RHASH_API int rhash_get_hash_length(unsigned hash_id) { const rhash_info* info = rhash_info_by_id(hash_id); @@ -550,26 +432,12 @@ RHASH_API int rhash_get_hash_length(unsigned hash_id) BASE32_LENGTH(info->digest_size) : info->digest_size * 2) : 0); } -/** - * Returns a name of given hash algorithm. - * - * @param hash_id the id of hash algorithm - * @return algorithm name - */ RHASH_API const char* rhash_get_name(unsigned hash_id) { const rhash_info* info = rhash_info_by_id(hash_id); return (info ? info->name : 0); } -/** - * Returns a name part of magnet urn of the given hash algorithm. - * Such magnet_name is used to generate a magnet link of the form - * urn:<magnet_name>=<hash_value>. - * - * @param hash_id the id of hash algorithm - * @return name - */ RHASH_API const char* rhash_get_magnet_name(unsigned hash_id) { const rhash_info* info = rhash_info_by_id(hash_id); @@ -599,7 +467,7 @@ static size_t rhash_get_magnet_url_size(const char* filepath, } if (filepath) { - size += 4 + rhash_urlencode(NULL, filepath); + size += 4 + rhash_urlencode(NULL, filepath, strlen(filepath), 0); } /* loop through hash values */ @@ -610,34 +478,20 @@ static size_t rhash_get_magnet_url_size(const char* filepath, size += (7 + 2) + strlen(name); size += rhash_print(NULL, context, bit, - (bit & (RHASH_SHA1 | RHASH_BTIH) ? RHPR_BASE32 : 0)); + (bit & RHASH_SHA1 ? RHPR_BASE32 : 0)); } return size; } -/** - * Print magnet link with given filepath and calculated hash sums into the - * output buffer. The hash_mask can limit which hash values will be printed. - * The function returns the size of the required buffer. - * If output is NULL the . - * - * @param output a string buffer to receive the magnet link or NULL - * @param filepath the file path to be printed or NULL - * @param context algorithms state - * @param hash_mask bit mask of the hash sums to add to the link - * @param flags can be combination of bits RHPR_UPPERCASE, RHPR_NO_MAGNET, - * RHPR_FILESIZE - * @return number of written characters, including terminating '\0' on success, 0 on fail - */ RHASH_API size_t rhash_print_magnet(char* output, const char* filepath, rhash context, unsigned hash_mask, int flags) { int i; const char* begin = output; - if (output == NULL) return rhash_get_magnet_url_size( - filepath, context, hash_mask, flags); + if (output == NULL) + return rhash_get_magnet_url_size(filepath, context, hash_mask, flags); /* RHPR_NO_MAGNET, RHPR_FILESIZE */ if ((flags & RHPR_NO_MAGNET) == 0) { @@ -652,13 +506,13 @@ RHASH_API size_t rhash_print_magnet(char* output, const char* filepath, *(output++) = '&'; } + flags &= RHPR_UPPERCASE; if (filepath) { strcpy(output, "dn="); output += 3; - output += rhash_urlencode(output, filepath); + output += rhash_urlencode(output, filepath, strlen(filepath), flags); *(output++) = '&'; } - flags &= RHPR_UPPERCASE; for (i = 0; i < 2; i++) { unsigned bit; @@ -679,7 +533,7 @@ RHASH_API size_t rhash_print_magnet(char* output, const char* filepath, output += strlen(name); *(output++) = ':'; output += rhash_print(output, context, bit, - (bit & (RHASH_SHA1 | RHASH_BTIH) ? flags | RHPR_BASE32 : flags)); + (bit & RHASH_SHA1 ? flags | RHPR_BASE32 : flags)); *(output++) = '&'; } } @@ -688,62 +542,39 @@ RHASH_API size_t rhash_print_magnet(char* output, const char* filepath, return (output - begin); } -/* hash sum output */ -/** - * Print a text presentation of a given hash sum to the specified buffer, - * - * @param output a buffer to print the hash to - * @param bytes a hash sum to print - * @param size a size of hash sum in bytes - * @param flags a bit-mask controlling how to format the hash sum, - * can be a mix of the flags: RHPR_RAW, RHPR_HEX, RHPR_BASE32, - * RHPR_BASE64, RHPR_UPPERCASE, RHPR_REVERSE - * @return the number of written characters - */ -size_t rhash_print_bytes(char* output, const unsigned char* bytes, - size_t size, int flags) +/* HASH SUM OUTPUT INTERFACE */ + +size_t rhash_print_bytes(char* output, const unsigned char* bytes, size_t size, int flags) { - size_t str_len; + size_t result_length; int upper_case = (flags & RHPR_UPPERCASE); int format = (flags & ~RHPR_MODIFIER); switch (format) { case RHPR_HEX: - str_len = size * 2; - rhash_byte_to_hex(output, bytes, (unsigned)size, upper_case); + result_length = size * 2; + rhash_byte_to_hex(output, bytes, size, upper_case); break; case RHPR_BASE32: - str_len = BASE32_LENGTH(size); - rhash_byte_to_base32(output, bytes, (unsigned)size, upper_case); + result_length = BASE32_LENGTH(size); + rhash_byte_to_base32(output, bytes, size, upper_case); break; case RHPR_BASE64: - str_len = BASE64_LENGTH(size); - rhash_byte_to_base64(output, bytes, (unsigned)size); + result_length = rhash_base64_url_encoded_helper(output, bytes, size, (flags & RHPR_URLENCODE), upper_case); break; default: - str_len = size; - memcpy(output, bytes, size); + if (flags & RHPR_URLENCODE) { + result_length = rhash_urlencode(output, (char*)bytes, size, upper_case); + } else { + memcpy(output, bytes, size); + result_length = size; + } break; } - return str_len; + return result_length; } -/** - * Print text presentation of a hash sum with given hash_id to the specified - * output buffer. If the hash_id is zero, then print the hash sum with - * the lowest id stored in the hash context. - * The function call fails if the context doesn't include a hash with the - * given hash_id. - * - * @param output a buffer to print the hash to - * @param context algorithms state - * @param hash_id id of the hash sum to print or 0 to print the first hash - * saved in the context. - * @param flags a bitmask controlling how to print the hash. Can contain flags - * RHPR_UPPERCASE, RHPR_HEX, RHPR_BASE32, RHPR_BASE64, etc. - * @return the number of written characters on success or 0 on fail - */ size_t RHASH_API rhash_print(char* output, rhash context, unsigned hash_id, int flags) { const rhash_info* info; @@ -764,15 +595,16 @@ size_t RHASH_API rhash_print(char* output, rhash context, unsigned hash_id, int } if (output == NULL) { + size_t multiplier = (flags & RHPR_URLENCODE ? 3 : 1); switch (flags & RHPR_FORMAT) { case RHPR_HEX: return (digest_size * 2); case RHPR_BASE32: return BASE32_LENGTH(digest_size); case RHPR_BASE64: - return BASE64_LENGTH(digest_size); + return BASE64_LENGTH(digest_size) * multiplier; default: - return digest_size; + return digest_size * multiplier; } } @@ -781,7 +613,8 @@ size_t RHASH_API rhash_print(char* output, rhash context, unsigned hash_id, int if ((flags & ~RHPR_UPPERCASE) == (RHPR_REVERSE | RHPR_HEX)) { /* reverse the digest */ - unsigned char *p = digest, *r = digest + digest_size - 1; + unsigned char* p = digest; + unsigned char* r = digest + digest_size - 1; char tmp; for (; p < r; p++, r--) { tmp = *p; @@ -793,7 +626,7 @@ size_t RHASH_API rhash_print(char* output, rhash context, unsigned hash_id, int return rhash_print_bytes(output, digest, digest_size, flags); } -#if defined(_WIN32) && defined(RHASH_EXPORTS) +#if (defined(_WIN32) || defined(__CYGWIN__)) && defined(RHASH_EXPORTS) #include BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved); BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) @@ -814,17 +647,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) } #endif -#define PVOID2UPTR(p) ((rhash_uptr_t)((char*)p - 0)) +#define PVOID2UPTR(p) ((rhash_uptr_t)(((char*)(p)) + 0)) -/** - * Process a rhash message. - * - * @param msg_id message identifier - * @param dst message destination (can be NULL for generic messages) - * @param ldata data depending on message - * @param rdata data depending on message - * @return message-specific data - */ RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t ldata, rhash_uptr_t rdata) { /* for messages working with rhash context */ @@ -865,6 +689,10 @@ RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t l case RMSG_GET_OPENSSL_MASK: return rhash_openssl_hash_mask; #endif + case RMSG_GET_OPENSSL_SUPPORTED_MASK: + return rhash_get_openssl_supported_hash_mask(); + case RMSG_GET_OPENSSL_AVAILABLE_MASK: + return rhash_get_openssl_available_hash_mask(); default: return RHASH_ERROR; /* unknown message */ diff --git a/Utilities/cmlibrhash/librhash/rhash.h b/Utilities/cmlibrhash/librhash/rhash.h index cee0e253e4c..c0117626eed 100644 --- a/Utilities/cmlibrhash/librhash/rhash.h +++ b/Utilities/cmlibrhash/librhash/rhash.h @@ -9,7 +9,9 @@ extern "C" { #endif #ifndef RHASH_API -/* modifier for LibRHash functions */ +/** + * Modifier for LibRHash functions + */ # define RHASH_API #endif @@ -32,11 +34,11 @@ enum rhash_ids RHASH_AICH = 0x100, RHASH_WHIRLPOOL = 0x200, RHASH_RIPEMD160 = 0x400, - RHASH_GOST = 0x800, - RHASH_GOST_CRYPTOPRO = 0x1000, - RHASH_HAS160 = 0x2000, - RHASH_SNEFRU128 = 0x4000, - RHASH_SNEFRU256 = 0x8000, + RHASH_GOST94 = 0x800, + RHASH_GOST94_CRYPTOPRO = 0x1000, + RHASH_HAS160 = 0x2000, + RHASH_GOST12_256 = 0x4000, + RHASH_GOST12_512 = 0x8000, RHASH_SHA224 = 0x10000, RHASH_SHA256 = 0x20000, RHASH_SHA384 = 0x40000, @@ -47,18 +49,28 @@ enum rhash_ids RHASH_SHA3_256 = 0x0800000, RHASH_SHA3_384 = 0x1000000, RHASH_SHA3_512 = 0x2000000, + RHASH_CRC32C = 0x4000000, + RHASH_SNEFRU128 = 0x8000000, + RHASH_SNEFRU256 = 0x10000000, - /** The bit-mask containing all supported hashe functions */ - RHASH_ALL_HASHES = RHASH_CRC32 | RHASH_MD4 | RHASH_MD5 | RHASH_ED2K | RHASH_SHA1 | - RHASH_TIGER | RHASH_TTH | RHASH_GOST | RHASH_GOST_CRYPTOPRO | + /** + * The bit-mask containing all supported hashe functions. + */ + RHASH_ALL_HASHES = RHASH_CRC32 | RHASH_CRC32C | RHASH_MD4 | RHASH_MD5 | + RHASH_ED2K | RHASH_SHA1 |RHASH_TIGER | RHASH_TTH | + RHASH_GOST94 | RHASH_GOST94_CRYPTOPRO | RHASH_GOST12_256 | RHASH_GOST12_512 | RHASH_BTIH | RHASH_AICH | RHASH_WHIRLPOOL | RHASH_RIPEMD160 | RHASH_HAS160 | RHASH_SNEFRU128 | RHASH_SNEFRU256 | RHASH_SHA224 | RHASH_SHA256 | RHASH_SHA384 | RHASH_SHA512 | RHASH_SHA3_224 | RHASH_SHA3_256 | RHASH_SHA3_384 | RHASH_SHA3_512 | RHASH_EDONR256 | RHASH_EDONR512, - /** The number of supported hash functions */ - RHASH_HASH_COUNT = 26 + RHASH_GOST = RHASH_GOST94, /* deprecated constant name */ + RHASH_GOST_CRYPTOPRO = RHASH_GOST94_CRYPTOPRO, /* deprecated constant name */ + /** + * The number of supported hash functions. + */ + RHASH_HASH_COUNT = 29 #else RHASH_MD5 = 0x01, RHASH_SHA1 = 0x02, @@ -86,15 +98,17 @@ enum rhash_ids }; /** - * The rhash context structure contains contexts for several hash functions + * The rhash context structure contains contexts for several hash functions. */ typedef struct rhash_context { - /** The size of the hashed message */ + /** + * The size of the hashed message. + */ unsigned long long msg_size; /** - * The bit-mask containing identifiers of the hashes being calculated + * The bit-mask containing identifiers of the hashes being calculated. */ unsigned hash_id; } rhash_context; @@ -107,109 +121,285 @@ typedef struct rhash_context typedef struct rhash_context* rhash; #endif /* LIBRHASH_RHASH_CTX_DEFINED */ -/** type of a callback to be called periodically while hashing a file */ +/** + * Type of a callback to be called periodically while hashing a file. + */ typedef void (*rhash_callback_t)(void* data, unsigned long long offset); -RHASH_API void rhash_library_init(void); /* initialize static data */ +/** + * Initialize static data of rhash algorithms + */ +RHASH_API void rhash_library_init(void); -/* hi-level hashing functions */ + +/* HIGH-LEVEL LIBRHASH INTERFACE */ + +/** + * Compute a hash of the given message. + * + * @param hash_id id of hash sum to compute + * @param message the message to process + * @param length message length + * @param result buffer to receive binary hash string + * @return 0 on success, -1 on error + */ RHASH_API int rhash_msg(unsigned hash_id, const void* message, size_t length, unsigned char* result); + +/** + * Compute a single hash for given file. + * + * @param hash_id id of hash sum to compute + * @param filepath path to the file to hash + * @param result buffer to receive hash value with the lowest requested id + * @return 0 on success, -1 on error and errno is set + */ RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result); -RHASH_API int rhash_file_update(rhash ctx, FILE* fd); -#ifdef _WIN32 /* windows only function */ +#ifdef _WIN32 +/** + * Compute a single hash for given file (Windows-specific function). + * + * @param hash_id id of hash sum to compute + * @param filepath path to the file to hash + * @param result buffer to receive hash value with the lowest requested id + * @return 0 on success, -1 on error, -1 on error and errno is set + */ RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned char* result); #endif -/* lo-level interface */ + +/* LOW-LEVEL LIBRHASH INTERFACE */ + +/** + * Allocate and initialize RHash context for calculating hash(es). + * After initializing rhash_update()/rhash_final() functions should be used. + * Then the context must be freed by calling rhash_free(). + * + * @param hash_id union of bit flags, containing ids of hashes to calculate. + * @return initialized rhash context, NULL on error and errno is set + */ RHASH_API rhash rhash_init(unsigned hash_id); -/*RHASH_API rhash rhash_init_by_ids(unsigned hash_ids[], unsigned count);*/ -RHASH_API int rhash_update(rhash ctx, const void* message, size_t length); -RHASH_API int rhash_final(rhash ctx, unsigned char* first_result); -RHASH_API void rhash_reset(rhash ctx); /* reinitialize the context */ + +/** + * Calculate hashes of message. + * Can be called repeatedly with chunks of the message to be hashed. + * + * @param ctx the rhash context + * @param message message chunk + * @param length length of the message chunk + * @return 0 on success; On fail return -1 and set errno + */ +RHASH_API int rhash_update(rhash ctx, const void* message, size_t length); + +/** + * Hash a file or stream. Multiple hashes can be computed. + * First, inintialize ctx parameter with rhash_init() before calling + * rhash_file_update(). Then use rhash_final() and rhash_print() + * to retrive hash values. Finaly call rhash_free() on ctx + * to free allocated memory or call rhash_reset() to reuse ctx. + * + * @param ctx rhash context + * @param fd descriptor of the file to hash + * @return 0 on success, -1 on error and errno is set + */ +RHASH_API int rhash_file_update(rhash ctx, FILE* fd); + +/** + * Finalize hash calculation and optionally store the first hash. + * + * @param ctx the rhash context + * @param first_result optional buffer to store a calculated hash with the lowest available id + * @return 0 on success; On fail return -1 and set errno + */ +RHASH_API int rhash_final(rhash ctx, unsigned char* first_result); + +/** + * Re-initialize RHash context to reuse it. + * Useful to speed up processing of many small messages. + * + * @param ctx context to reinitialize + */ +RHASH_API void rhash_reset(rhash ctx); + +/** + * Free RHash context memory. + * + * @param ctx the context to free. + */ RHASH_API void rhash_free(rhash ctx); -/* additional lo-level functions */ +/** + * Set the callback function to be called from the + * rhash_file() and rhash_file_update() functions + * on processing every file block. The file block + * size is set internally by rhash and now is 8 KiB. + * + * @param ctx rhash context + * @param callback pointer to the callback function + * @param callback_data pointer to data passed to the callback + */ RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data); -/** bit-flag: default hash output format is base32 */ -#define RHASH_INFO_BASE32 1 + +/* INFORMATION FUNCTIONS */ /** - * Information about a hash function. + * Returns the number of supported hash algorithms. + * + * @return the number of supported hash functions */ -typedef struct rhash_info -{ - /** hash function indentifier */ - unsigned hash_id; - /** flags bit-mask, including RHASH_INFO_BASE32 bit */ - unsigned flags; - /** size of binary message digest in bytes */ - size_t digest_size; - const char* name; - const char* magnet_name; -} rhash_info; - -/* information functions */ RHASH_API int rhash_count(void); /* number of supported hashes */ + +/** + * Returns size of binary digest for given hash algorithm. + * + * @param hash_id the id of hash algorithm + * @return digest size in bytes + */ RHASH_API int rhash_get_digest_size(unsigned hash_id); /* size of binary message digest */ + +/** + * Returns length of digest hash string in default output format. + * + * @param hash_id the id of hash algorithm + * @return the length of hash string + */ RHASH_API int rhash_get_hash_length(unsigned hash_id); /* length of formatted hash string */ + +/** + * Detect default digest output format for given hash algorithm. + * + * @param hash_id the id of hash algorithm + * @return 1 for base32 format, 0 for hexadecimal + */ RHASH_API int rhash_is_base32(unsigned hash_id); /* default digest output format */ + +/** + * Returns a name of given hash algorithm. + * + * @param hash_id the id of hash algorithm + * @return algorithm name + */ RHASH_API const char* rhash_get_name(unsigned hash_id); /* get hash function name */ + +/** + * Returns a name part of magnet urn of the given hash algorithm. + * Such magnet_name is used to generate a magnet link of the form + * urn:<magnet_name>=<hash_value>. + * + * @param hash_id the id of hash algorithm + * @return name + */ RHASH_API const char* rhash_get_magnet_name(unsigned hash_id); /* get name part of magnet urn */ -/* note, that rhash_info_by_id() is not exported to a shared library or DLL */ -const rhash_info* rhash_info_by_id(unsigned hash_id); /* get hash sum info by hash id */ +/* HASH SUM OUTPUT INTERFACE */ #if 0 /** - * Flags for printing a hash sum + * Flags for printing a hash sum. */ enum rhash_print_sum_flags { - /** print in a default format */ + /* + * Print in a default format + */ RHPR_DEFAULT = 0x0, - /** output as binary message digest */ + /* + * Output as binary message digest + */ RHPR_RAW = 0x1, - /** print as a hexadecimal string */ + /* + * Print as a hexadecimal string + */ RHPR_HEX = 0x2, - /** print as a base32-encoded string */ + /* + * Print as a base32-encoded string + */ RHPR_BASE32 = 0x3, - /** print as a base64-encoded string */ + /* + * Print as a base64-encoded string + */ RHPR_BASE64 = 0x4, - - /** + /* * Print as an uppercase string. Can be used * for base32 or hexadecimal format only. */ RHPR_UPPERCASE = 0x8, - - /** + /* * Reverse hash bytes. Can be used for GOST hash. */ RHPR_REVERSE = 0x10, - - /** don't print 'magnet:?' prefix in rhash_print_magnet */ + /* + * Don't print 'magnet:?' prefix in rhash_print_magnet + */ RHPR_NO_MAGNET = 0x20, - /** print file size in rhash_print_magnet */ + /* + * Print file size in rhash_print_magnet + */ RHPR_FILESIZE = 0x40, + /* + * Print as URL-encoded string + */ + RHPR_URLENCODE = 0x80 }; #endif -/* output hash into the given buffer */ + +/** + * Print a text presentation of a given hash sum to the specified buffer. + * + * @param output a buffer to print the hash to + * @param bytes a hash sum to print + * @param size a size of hash sum in bytes + * @param flags a bit-mask controlling how to format the hash sum, + * can be a mix of the flags: RHPR_RAW, RHPR_HEX, RHPR_BASE32, + * RHPR_BASE64, RHPR_URLENCODE, RHPR_UPPERCASE, RHPR_REVERSE + * @return the number of written characters + */ RHASH_API size_t rhash_print_bytes(char* output, const unsigned char* bytes, size_t size, int flags); +/** + * Print text presentation of a hash sum with given hash_id to the specified + * output buffer. If the hash_id is zero, then print the hash sum with + * the lowest id stored in the hash context. + * The function call fails if the context doesn't include a hash with the + * given hash_id. + * + * @param output a buffer to print the hash to + * @param ctx algorithms state + * @param hash_id id of the hash sum to print or 0 to print the first hash + * saved in the context. + * @param flags a bitmask controlling how to print the hash. Can contain flags + * RHPR_UPPERCASE, RHPR_HEX, RHPR_BASE32, RHPR_BASE64, etc. + * @return the number of written characters on success or 0 on fail + */ RHASH_API size_t rhash_print(char* output, rhash ctx, unsigned hash_id, int flags); -/* output magnet URL into the given buffer */ +/** + * Print magnet link with given filepath and calculated hash sums into the + * output buffer. The hash_mask can limit which hash values will be printed. + * The function returns the size of the required buffer. + * If output is NULL the . + * + * @param output a string buffer to receive the magnet link or NULL + * @param filepath the file path to be printed or NULL + * @param context algorithms state + * @param hash_mask bit mask of the hash sums to add to the link + * @param flags can be combination of bits RHPR_UPPERCASE, RHPR_NO_MAGNET, + * RHPR_FILESIZE + * @return number of written characters, including terminating '\0' on success, 0 on fail + */ RHASH_API size_t rhash_print_magnet(char* output, const char* filepath, rhash context, unsigned hash_mask, int flags); -/* macros for message API */ -/** The type of an unsigned integer large enough to hold a pointer */ +/* MESSAGE API */ + +/** + * The type of an unsigned integer large enough to hold a pointer. + */ #if defined(UINTPTR_MAX) typedef uintptr_t rhash_uptr_t; #elif defined(_LP64) || defined(__LP64__) || defined(__x86_64) || \ @@ -219,14 +409,28 @@ typedef unsigned long long rhash_uptr_t; typedef unsigned long rhash_uptr_t; #endif -/** The value returned by rhash_transmit on error */ +/** + * The value returned by rhash_transmit on error. + */ #define RHASH_ERROR ((rhash_uptr_t)-1) -/** Convert a pointer to rhash_uptr_t */ +/** + * Convert a pointer to rhash_uptr_t. + */ #define RHASH_STR2UPTR(str) ((rhash_uptr_t)(char*)(str)) -/** Convert a rhash_uptr_t to a void* pointer */ -#define RHASH_UPTR2PVOID(u) ((void*)((char*)0 + (u))) +/** + * Convert a rhash_uptr_t to a void* pointer. + */ +#define RHASH_UPTR2PVOID(u) ((void*)((u) + 0)) -/* rhash API to set/get data via messages */ +/** + * Process a rhash message. + * + * @param msg_id message identifier + * @param dst message destination (can be NULL for generic messages) + * @param ldata data depending on message + * @param rdata data depending on message + * @return message-specific data + */ RHASH_API rhash_uptr_t rhash_transmit( unsigned msg_id, void* dst, rhash_uptr_t ldata, rhash_uptr_t rdata); @@ -239,22 +443,32 @@ RHASH_API rhash_uptr_t rhash_transmit( #define RMSG_SET_AUTOFINAL 5 #define RMSG_SET_OPENSSL_MASK 10 #define RMSG_GET_OPENSSL_MASK 11 +#define RMSG_GET_OPENSSL_SUPPORTED_MASK 12 +#define RMSG_GET_OPENSSL_AVAILABLE_MASK 13 -/* helper macros */ +/* HELPER MACROS */ -/** Get a pointer to context of the specified hash function */ +/** + * Get a pointer to context of the specified hash function. + */ #define rhash_get_context_ptr(ctx, hash_id) RHASH_UPTR2PVOID(rhash_transmit(RMSG_GET_CONTEXT, ctx, hash_id, 0)) -/** Cancel hash calculation of a file */ +/** + * Cancel hash calculation of a file. + */ #define rhash_cancel(ctx) rhash_transmit(RMSG_CANCEL, ctx, 0, 0) -/** Return non-zero if hash calculation was canceled, zero otherwise */ +/** + * Return non-zero if hash calculation was canceled, zero otherwise. + */ #define rhash_is_canceled(ctx) rhash_transmit(RMSG_IS_CANCELED, ctx, 0, 0) -/** Return non-zero if rhash_final was called for rhash_context */ +/** + * Return non-zero if rhash_final was called for rhash_context. + */ #define rhash_get_finalized(ctx) rhash_transmit(RMSG_GET_FINALIZED, ctx, 0, 0) /** * Turn on/off the auto-final flag for the given rhash_context. By default * auto-final is on, which means rhash_final is called automatically, if - * needed when a hash value is retrived by rhash_print call. + * needed when a hash value is retrieved by rhash_print call. */ #define rhash_set_autofinal(ctx, on) rhash_transmit(RMSG_SET_AUTOFINAL, ctx, on, 0) @@ -267,19 +481,36 @@ RHASH_API rhash_uptr_t rhash_transmit( #define rhash_set_openssl_mask(mask) rhash_transmit(RMSG_SET_OPENSSL_MASK, NULL, mask, 0) /** - * Return current bit-mask of hash algorithms selected to be calculated - * by OpenSSL library. + * Return current bit-mask of hash algorithms selected to be calculated by OpenSSL + * library. Return RHASH_ERROR if LibRHash is compiled without OpenSSL support. */ #define rhash_get_openssl_mask() rhash_transmit(RMSG_GET_OPENSSL_MASK, NULL, 0, 0) -/** The bit mask of hash algorithms implemented by OpenSSL */ -#if defined(USE_OPENSSL) || defined(OPENSSL_RUNTIME) -# define RHASH_OPENSSL_SUPPORTED_HASHES (RHASH_MD4 | RHASH_MD5 | \ - RHASH_SHA1 | RHASH_SHA224 | RHASH_SHA256 | RHASH_SHA384 | \ - RHASH_SHA512 | RHASH_RIPEMD160 | RHASH_WHIRLPOOL) -#else -# define RHASH_OPENSSL_SUPPORTED_HASHES 0 -#endif +/** + * Return the bit-mask of algorithms that can be provided by the OpenSSL plugin, + * if the library is compiled with OpenSSL support, 0 otherwise. This bit-mask is + * a constant value computed at compile-time. + */ +#define rhash_get_openssl_supported_mask() rhash_transmit(RMSG_GET_OPENSSL_SUPPORTED_MASK, NULL, 0, 0) + +/** + * Return the bit-mask of algorithms that are successfully loaded from + * OpenSSL library. If the library is not loaded or not supported by LibRHash, + * then return 0. + */ +#define rhash_get_openssl_available_mask() rhash_transmit(RMSG_GET_OPENSSL_AVAILABLE_MASK, NULL, 0, 0) + + +/** + * Return non-zero if LibRHash hash been compiled with OpenSSL support, + * and zero otherwise. + */ +#define rhash_is_openssl_supported() (rhash_get_openssl_mask() != RHASH_ERROR) + +/** + * Legacy macro. The bit mask of hash algorithms implemented by OpenSSL. + */ +# define RHASH_OPENSSL_SUPPORTED_HASHES (rhash_get_openssl_supported_mask()) #ifdef __cplusplus } /* extern "C" */ diff --git a/Utilities/cmlibrhash/librhash/sha1.c b/Utilities/cmlibrhash/librhash/sha1.c index f5a053be2dc..b226925f051 100644 --- a/Utilities/cmlibrhash/librhash/sha1.c +++ b/Utilities/cmlibrhash/librhash/sha1.c @@ -1,18 +1,18 @@ /* sha1.c - an implementation of Secure Hash Algorithm 1 (SHA1) * based on RFC 3174. * - * Copyright: 2008-2012 Aleksey Kravchenko + * Copyright (c) 2008, Aleksey Kravchenko * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ #include @@ -24,7 +24,7 @@ * * @param ctx context to initialize */ -void rhash_sha1_init(sha1_ctx *ctx) +void rhash_sha1_init(sha1_ctx* ctx) { ctx->length = 0; @@ -121,7 +121,7 @@ static void rhash_sha1_process_block(unsigned* hash, const unsigned* block) * @param msg message chunk * @param size length of the message chunk */ -void rhash_sha1_update(sha1_ctx *ctx, const unsigned char* msg, size_t size) +void rhash_sha1_update(sha1_ctx* ctx, const unsigned char* msg, size_t size) { unsigned index = (unsigned)ctx->length & 63; ctx->length += size; @@ -164,7 +164,7 @@ void rhash_sha1_update(sha1_ctx *ctx, const unsigned char* msg, size_t size) * @param ctx the algorithm context containing current hashing state * @param result calculated hash in binary form */ -void rhash_sha1_final(sha1_ctx *ctx, unsigned char* result) +void rhash_sha1_final(sha1_ctx* ctx, unsigned char* result) { unsigned index = (unsigned)ctx->length & 63; unsigned* msg32 = (unsigned*)ctx->message; diff --git a/Utilities/cmlibrhash/librhash/sha1.h b/Utilities/cmlibrhash/librhash/sha1.h index 74b2f948f81..7e995424073 100644 --- a/Utilities/cmlibrhash/librhash/sha1.h +++ b/Utilities/cmlibrhash/librhash/sha1.h @@ -20,9 +20,9 @@ typedef struct sha1_ctx /* hash functions */ -void rhash_sha1_init(sha1_ctx *ctx); -void rhash_sha1_update(sha1_ctx *ctx, const unsigned char* msg, size_t size); -void rhash_sha1_final(sha1_ctx *ctx, unsigned char* result); +void rhash_sha1_init(sha1_ctx* ctx); +void rhash_sha1_update(sha1_ctx* ctx, const unsigned char* msg, size_t size); +void rhash_sha1_final(sha1_ctx* ctx, unsigned char* result); #ifdef __cplusplus } /* extern "C" */ diff --git a/Utilities/cmlibrhash/librhash/sha256.c b/Utilities/cmlibrhash/librhash/sha256.c index af5b0fe5484..21a69aae238 100644 --- a/Utilities/cmlibrhash/librhash/sha256.c +++ b/Utilities/cmlibrhash/librhash/sha256.c @@ -1,18 +1,18 @@ /* sha256.c - an implementation of SHA-256/224 hash functions * based on FIPS 180-3 (Federal Information Processing Standart). * - * Copyright: 2010-2012 Aleksey Kravchenko + * Copyright (c) 2010, Aleksey Kravchenko * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ #include @@ -65,7 +65,7 @@ static const unsigned rhash_k256[64] = { * * @param ctx context to initialize */ -void rhash_sha256_init(sha256_ctx *ctx) +void rhash_sha256_init(sha256_ctx* ctx) { /* Initial values. These words were obtained by taking the first 32 * bits of the fractional parts of the square roots of the first @@ -87,7 +87,7 @@ void rhash_sha256_init(sha256_ctx *ctx) * * @param ctx context to initialize */ -void rhash_sha224_init(struct sha256_ctx *ctx) +void rhash_sha224_init(struct sha256_ctx* ctx) { /* Initial values from FIPS 180-3. These words were obtained by taking * bits from 33th to 64th of the fractional parts of the square @@ -113,7 +113,7 @@ static void rhash_sha256_process_block(unsigned hash[8], unsigned block[16]) { unsigned A, B, C, D, E, F, G, H; unsigned W[16]; - const unsigned *k; + const unsigned* k; int i; A = hash[0], B = hash[1], C = hash[2], D = hash[3]; @@ -168,7 +168,7 @@ static void rhash_sha256_process_block(unsigned hash[8], unsigned block[16]) * @param msg message chunk * @param size length of the message chunk */ -void rhash_sha256_update(sha256_ctx *ctx, const unsigned char *msg, size_t size) +void rhash_sha256_update(sha256_ctx* ctx, const unsigned char* msg, size_t size) { size_t index = (size_t)ctx->length & 63; ctx->length += size; @@ -210,7 +210,7 @@ void rhash_sha256_update(sha256_ctx *ctx, const unsigned char *msg, size_t size) * @param ctx the algorithm context containing current hashing state * @param result calculated hash in binary form */ -void rhash_sha256_final(sha256_ctx *ctx, unsigned char* result) +void rhash_sha256_final(sha256_ctx* ctx, unsigned char* result) { size_t index = ((unsigned)ctx->length & 63) >> 2; unsigned shift = ((unsigned)ctx->length & 3) * 8; diff --git a/Utilities/cmlibrhash/librhash/sha256.h b/Utilities/cmlibrhash/librhash/sha256.h index f87ebaa6975..3625cfe2029 100644 --- a/Utilities/cmlibrhash/librhash/sha256.h +++ b/Utilities/cmlibrhash/librhash/sha256.h @@ -20,10 +20,10 @@ typedef struct sha256_ctx unsigned digest_length; /* length of the algorithm digest in bytes */ } sha256_ctx; -void rhash_sha224_init(sha256_ctx *ctx); -void rhash_sha256_init(sha256_ctx *ctx); -void rhash_sha256_update(sha256_ctx *ctx, const unsigned char* data, size_t length); -void rhash_sha256_final(sha256_ctx *ctx, unsigned char result[32]); +void rhash_sha224_init(sha256_ctx* ctx); +void rhash_sha256_init(sha256_ctx* ctx); +void rhash_sha256_update(sha256_ctx* ctx, const unsigned char* data, size_t length); +void rhash_sha256_final(sha256_ctx* ctx, unsigned char result[32]); #ifdef __cplusplus } /* extern "C" */ diff --git a/Utilities/cmlibrhash/librhash/sha3.c b/Utilities/cmlibrhash/librhash/sha3.c index e4a845f6623..bd2854f5f2f 100644 --- a/Utilities/cmlibrhash/librhash/sha3.c +++ b/Utilities/cmlibrhash/librhash/sha3.c @@ -3,18 +3,18 @@ * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche * - * Copyright: 2013 Aleksey Kravchenko + * Copyright (c) 2013, Aleksey Kravchenko * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ #include @@ -36,7 +36,7 @@ static uint64_t keccak_round_constants[NumberOfRounds] = { }; /* Initializing a sha3 context for given number of output bits */ -static void rhash_keccak_init(sha3_ctx *ctx, unsigned bits) +static void rhash_keccak_init(sha3_ctx* ctx, unsigned bits) { /* NB: The Keccak capacity parameter = bits * 2 */ unsigned rate = 1600 - bits * 2; @@ -51,7 +51,7 @@ static void rhash_keccak_init(sha3_ctx *ctx, unsigned bits) * * @param ctx context to initialize */ -void rhash_sha3_224_init(sha3_ctx *ctx) +void rhash_sha3_224_init(sha3_ctx* ctx) { rhash_keccak_init(ctx, 224); } @@ -61,7 +61,7 @@ void rhash_sha3_224_init(sha3_ctx *ctx) * * @param ctx context to initialize */ -void rhash_sha3_256_init(sha3_ctx *ctx) +void rhash_sha3_256_init(sha3_ctx* ctx) { rhash_keccak_init(ctx, 256); } @@ -71,7 +71,7 @@ void rhash_sha3_256_init(sha3_ctx *ctx) * * @param ctx context to initialize */ -void rhash_sha3_384_init(sha3_ctx *ctx) +void rhash_sha3_384_init(sha3_ctx* ctx) { rhash_keccak_init(ctx, 384); } @@ -81,37 +81,37 @@ void rhash_sha3_384_init(sha3_ctx *ctx) * * @param ctx context to initialize */ -void rhash_sha3_512_init(sha3_ctx *ctx) +void rhash_sha3_512_init(sha3_ctx* ctx) { rhash_keccak_init(ctx, 512); } +#define XORED_A(i) A[(i)] ^ A[(i) + 5] ^ A[(i) + 10] ^ A[(i) + 15] ^ A[(i) + 20] +#define THETA_STEP(i) \ + A[(i)] ^= D[(i)]; \ + A[(i) + 5] ^= D[(i)]; \ + A[(i) + 10] ^= D[(i)]; \ + A[(i) + 15] ^= D[(i)]; \ + A[(i) + 20] ^= D[(i)] \ + /* Keccak theta() transformation */ -static void keccak_theta(uint64_t *A) +static void keccak_theta(uint64_t* A) { - unsigned int x; - uint64_t C[5], D[5]; - - for (x = 0; x < 5; x++) { - C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; - } - D[0] = ROTL64(C[1], 1) ^ C[4]; - D[1] = ROTL64(C[2], 1) ^ C[0]; - D[2] = ROTL64(C[3], 1) ^ C[1]; - D[3] = ROTL64(C[4], 1) ^ C[2]; - D[4] = ROTL64(C[0], 1) ^ C[3]; - - for (x = 0; x < 5; x++) { - A[x] ^= D[x]; - A[x + 5] ^= D[x]; - A[x + 10] ^= D[x]; - A[x + 15] ^= D[x]; - A[x + 20] ^= D[x]; - } + uint64_t D[5]; + D[0] = ROTL64(XORED_A(1), 1) ^ XORED_A(4); + D[1] = ROTL64(XORED_A(2), 1) ^ XORED_A(0); + D[2] = ROTL64(XORED_A(3), 1) ^ XORED_A(1); + D[3] = ROTL64(XORED_A(4), 1) ^ XORED_A(2); + D[4] = ROTL64(XORED_A(0), 1) ^ XORED_A(3); + THETA_STEP(0); + THETA_STEP(1); + THETA_STEP(2); + THETA_STEP(3); + THETA_STEP(4); } /* Keccak pi() transformation */ -static void keccak_pi(uint64_t *A) +static void keccak_pi(uint64_t* A) { uint64_t A1; A1 = A[1]; @@ -142,21 +142,27 @@ static void keccak_pi(uint64_t *A) /* note: A[ 0] is left as is */ } +#define CHI_STEP(i) \ + A0 = A[0 + (i)]; \ + A1 = A[1 + (i)]; \ + A[0 + (i)] ^= ~A1 & A[2 + (i)]; \ + A[1 + (i)] ^= ~A[2 + (i)] & A[3 + (i)]; \ + A[2 + (i)] ^= ~A[3 + (i)] & A[4 + (i)]; \ + A[3 + (i)] ^= ~A[4 + (i)] & A0; \ + A[4 + (i)] ^= ~A0 & A1 \ + /* Keccak chi() transformation */ -static void keccak_chi(uint64_t *A) +static void keccak_chi(uint64_t* A) { - int i; - for (i = 0; i < 25; i += 5) { - uint64_t A0 = A[0 + i], A1 = A[1 + i]; - A[0 + i] ^= ~A1 & A[2 + i]; - A[1 + i] ^= ~A[2 + i] & A[3 + i]; - A[2 + i] ^= ~A[3 + i] & A[4 + i]; - A[3 + i] ^= ~A[4 + i] & A0; - A[4 + i] ^= ~A0 & A1; - } + uint64_t A0, A1; + CHI_STEP(0); + CHI_STEP(5); + CHI_STEP(10); + CHI_STEP(15); + CHI_STEP(20); } -static void rhash_sha3_permutation(uint64_t *state) +static void rhash_sha3_permutation(uint64_t* state) { int round; for (round = 0; round < NumberOfRounds; round++) @@ -204,7 +210,7 @@ static void rhash_sha3_permutation(uint64_t *state) * @param block the message block to process * @param block_size the size of the processed block in bytes */ -static void rhash_sha3_process_block(uint64_t hash[25], const uint64_t *block, size_t block_size) +static void rhash_sha3_process_block(uint64_t hash[25], const uint64_t* block, size_t block_size) { /* expanded loop */ hash[ 0] ^= le2me_64(block[ 0]); @@ -260,7 +266,7 @@ static void rhash_sha3_process_block(uint64_t hash[25], const uint64_t *block, s * @param msg message chunk * @param size length of the message chunk */ -void rhash_sha3_update(sha3_ctx *ctx, const unsigned char *msg, size_t size) +void rhash_sha3_update(sha3_ctx* ctx, const unsigned char* msg, size_t size) { size_t index = (size_t)ctx->rest; size_t block_size = (size_t)ctx->block_size; @@ -305,7 +311,7 @@ void rhash_sha3_update(sha3_ctx *ctx, const unsigned char *msg, size_t size) * @param ctx the algorithm context containing current hashing state * @param result calculated hash in binary form */ -void rhash_sha3_final(sha3_ctx *ctx, unsigned char* result) +void rhash_sha3_final(sha3_ctx* ctx, unsigned char* result) { size_t digest_length = 100 - ctx->block_size / 2; const size_t block_size = ctx->block_size; @@ -333,7 +339,7 @@ void rhash_sha3_final(sha3_ctx *ctx, unsigned char* result) * @param ctx the algorithm context containing current hashing state * @param result calculated hash in binary form */ -void rhash_keccak_final(sha3_ctx *ctx, unsigned char* result) +void rhash_keccak_final(sha3_ctx* ctx, unsigned char* result) { size_t digest_length = 100 - ctx->block_size / 2; const size_t block_size = ctx->block_size; diff --git a/Utilities/cmlibrhash/librhash/sha3.h b/Utilities/cmlibrhash/librhash/sha3.h index 28319978d68..e00041d59e5 100644 --- a/Utilities/cmlibrhash/librhash/sha3.h +++ b/Utilities/cmlibrhash/librhash/sha3.h @@ -31,12 +31,12 @@ typedef struct sha3_ctx /* methods for calculating the hash function */ -void rhash_sha3_224_init(sha3_ctx *ctx); -void rhash_sha3_256_init(sha3_ctx *ctx); -void rhash_sha3_384_init(sha3_ctx *ctx); -void rhash_sha3_512_init(sha3_ctx *ctx); -void rhash_sha3_update(sha3_ctx *ctx, const unsigned char* msg, size_t size); -void rhash_sha3_final(sha3_ctx *ctx, unsigned char* result); +void rhash_sha3_224_init(sha3_ctx* ctx); +void rhash_sha3_256_init(sha3_ctx* ctx); +void rhash_sha3_384_init(sha3_ctx* ctx); +void rhash_sha3_512_init(sha3_ctx* ctx); +void rhash_sha3_update(sha3_ctx* ctx, const unsigned char* msg, size_t size); +void rhash_sha3_final(sha3_ctx* ctx, unsigned char* result); #ifdef USE_KECCAK #define rhash_keccak_224_init rhash_sha3_224_init @@ -44,7 +44,7 @@ void rhash_sha3_final(sha3_ctx *ctx, unsigned char* result); #define rhash_keccak_384_init rhash_sha3_384_init #define rhash_keccak_512_init rhash_sha3_512_init #define rhash_keccak_update rhash_sha3_update -void rhash_keccak_final(sha3_ctx *ctx, unsigned char* result); +void rhash_keccak_final(sha3_ctx* ctx, unsigned char* result); #endif #ifdef __cplusplus diff --git a/Utilities/cmlibrhash/librhash/sha512.c b/Utilities/cmlibrhash/librhash/sha512.c index a3e681d1f9f..555e6ef596d 100644 --- a/Utilities/cmlibrhash/librhash/sha512.c +++ b/Utilities/cmlibrhash/librhash/sha512.c @@ -1,18 +1,18 @@ /* sha512.c - an implementation of SHA-384/512 hash functions * based on FIPS 180-3 (Federal Information Processing Standart). * - * Copyright: 2010-2012 Aleksey Kravchenko + * Copyright (c) 2010, Aleksey Kravchenko * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ #include @@ -81,7 +81,7 @@ static const uint64_t rhash_k512[80] = { * * @param ctx context to initialize */ -void rhash_sha512_init(sha512_ctx *ctx) +void rhash_sha512_init(sha512_ctx* ctx) { /* Initial values. These words were obtained by taking the first 32 * bits of the fractional parts of the square roots of the first @@ -104,7 +104,7 @@ void rhash_sha512_init(sha512_ctx *ctx) * * @param ctx context to initialize */ -void rhash_sha384_init(struct sha512_ctx *ctx) +void rhash_sha384_init(struct sha512_ctx* ctx) { /* Initial values from FIPS 180-3. These words were obtained by taking * the first sixty-four bits of the fractional parts of the square @@ -131,7 +131,7 @@ static void rhash_sha512_process_block(uint64_t hash[8], uint64_t block[16]) { uint64_t A, B, C, D, E, F, G, H; uint64_t W[16]; - const uint64_t *k; + const uint64_t* k; int i; A = hash[0], B = hash[1], C = hash[2], D = hash[3]; @@ -186,7 +186,7 @@ static void rhash_sha512_process_block(uint64_t hash[8], uint64_t block[16]) * @param msg message chunk * @param size length of the message chunk */ -void rhash_sha512_update(sha512_ctx *ctx, const unsigned char *msg, size_t size) +void rhash_sha512_update(sha512_ctx* ctx, const unsigned char* msg, size_t size) { size_t index = (size_t)ctx->length & 127; ctx->length += size; @@ -228,7 +228,7 @@ void rhash_sha512_update(sha512_ctx *ctx, const unsigned char *msg, size_t size) * @param ctx the algorithm context containing current hashing state * @param result calculated hash in binary form */ -void rhash_sha512_final(sha512_ctx *ctx, unsigned char* result) +void rhash_sha512_final(sha512_ctx* ctx, unsigned char* result) { size_t index = ((unsigned)ctx->length & 127) >> 3; unsigned shift = ((unsigned)ctx->length & 7) * 8; diff --git a/Utilities/cmlibrhash/librhash/sha512.h b/Utilities/cmlibrhash/librhash/sha512.h index 7c689bea17a..f80ae0d8ccb 100644 --- a/Utilities/cmlibrhash/librhash/sha512.h +++ b/Utilities/cmlibrhash/librhash/sha512.h @@ -20,10 +20,10 @@ typedef struct sha512_ctx unsigned digest_length; /* length of the algorithm digest in bytes */ } sha512_ctx; -void rhash_sha384_init(sha512_ctx *ctx); -void rhash_sha512_init(sha512_ctx *ctx); -void rhash_sha512_update(sha512_ctx *ctx, const unsigned char* data, size_t length); -void rhash_sha512_final(sha512_ctx *ctx, unsigned char* result); +void rhash_sha384_init(sha512_ctx* ctx); +void rhash_sha512_init(sha512_ctx* ctx); +void rhash_sha512_update(sha512_ctx* ctx, const unsigned char* data, size_t length); +void rhash_sha512_final(sha512_ctx* ctx, unsigned char* result); #ifdef __cplusplus } /* extern "C" */ diff --git a/Utilities/cmlibrhash/librhash/ustd.h b/Utilities/cmlibrhash/librhash/ustd.h index 019b931931f..756ce0b8fe0 100644 --- a/Utilities/cmlibrhash/librhash/ustd.h +++ b/Utilities/cmlibrhash/librhash/ustd.h @@ -9,7 +9,7 @@ # pragma warning(push,1) #endif -#include +#include #ifndef KWIML_INT_HAVE_INT64_T # define int64_t KWIML_INT_int64_t @@ -36,4 +36,36 @@ # define uint8_t KWIML_INT_uint8_t #endif +#include + +#if 0 +#if _MSC_VER > 1000 +# include /* size_t for vc6.0 */ + +# if _MSC_VER >= 1600 +/* Visual Studio >= 2010 has stdint.h */ +# include +# else + /* vc6.0 has bug with __int8, so using char instead */ + typedef signed char int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef signed __int64 int64_t; + typedef unsigned char uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; +# endif /* _MSC_VER >= 1600 */ + +/* disable warnings: The POSIX name for this item is deprecated. Use the ISO C++ conformant name. */ +# pragma warning(disable : 4996) + +#else /* _MSC_VER > 1000 */ + +# include +# include + +#endif /* _MSC_VER > 1000 */ +#endif + #endif /* LIBRHASH_USTD_H */ diff --git a/Utilities/cmlibrhash/librhash/util.h b/Utilities/cmlibrhash/librhash/util.h index 9f371574a70..57cae9b5311 100644 --- a/Utilities/cmlibrhash/librhash/util.h +++ b/Utilities/cmlibrhash/librhash/util.h @@ -20,7 +20,7 @@ extern "C" { # define atomic_compare_and_swap(ptr, oldval, newval) atomic_cas_32(ptr, oldval, newval) #else /* pray that it will work */ -# define atomic_compare_and_swap(ptr, oldval, newval) { if(*(ptr) == (oldval)) *(ptr) = (newval); } +# define atomic_compare_and_swap(ptr, oldval, newval) { if (*(ptr) == (oldval)) *(ptr) = (newval); } # define NO_ATOMIC_BUILTINS #endif diff --git a/Utilities/cmlibuv/include/uv.h b/Utilities/cmlibuv/include/uv.h index eb80bfbc2cd..6f32b48777e 100644 --- a/Utilities/cmlibuv/include/uv.h +++ b/Utilities/cmlibuv/include/uv.h @@ -31,6 +31,10 @@ extern "C" { #endif +#if defined(BUILDING_UV_SHARED) && defined(USING_UV_SHARED) +#error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both." +#endif + #ifdef _WIN32 /* Windows - set up dll import/export decorators. */ # if defined(BUILDING_UV_SHARED) @@ -147,6 +151,7 @@ extern "C" { XX(EREMOTEIO, "remote I/O error") \ XX(ENOTTY, "inappropriate ioctl for device") \ XX(EFTYPE, "inappropriate file type or format") \ + XX(EILSEQ, "illegal byte sequence") \ #define UV_HANDLE_TYPE_MAP(XX) \ XX(ASYNC, async) \ @@ -176,6 +181,7 @@ extern "C" { XX(WORK, work) \ XX(GETADDRINFO, getaddrinfo) \ XX(GETNAMEINFO, getnameinfo) \ + XX(RANDOM, random) \ typedef enum { #define XX(code, _) UV_ ## code = UV__ ## code, @@ -233,13 +239,16 @@ typedef struct uv_connect_s uv_connect_t; typedef struct uv_udp_send_s uv_udp_send_t; typedef struct uv_fs_s uv_fs_t; typedef struct uv_work_s uv_work_t; +typedef struct uv_random_s uv_random_t; /* None of the above. */ +typedef struct uv_env_item_s uv_env_item_t; typedef struct uv_cpu_info_s uv_cpu_info_t; typedef struct uv_interface_address_s uv_interface_address_t; typedef struct uv_dirent_s uv_dirent_t; typedef struct uv_passwd_s uv_passwd_t; typedef struct uv_utsname_s uv_utsname_t; +typedef struct uv_statfs_s uv_statfs_t; typedef enum { UV_LOOP_BLOCK_SIGNAL @@ -260,6 +269,8 @@ typedef void* (*uv_realloc_func)(void* ptr, size_t size); typedef void* (*uv_calloc_func)(size_t count, size_t size); typedef void (*uv_free_func)(void* ptr); +UV_EXTERN void uv_library_shutdown(void); + UV_EXTERN int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, @@ -327,6 +338,10 @@ typedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req, int status, const char* hostname, const char* service); +typedef void (*uv_random_cb)(uv_random_t* req, + int status, + void* buf, + size_t buflen); typedef struct { long tv_sec; @@ -561,6 +576,7 @@ UV_EXTERN int uv_tcp_getsockname(const uv_tcp_t* handle, UV_EXTERN int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen); +UV_EXTERN int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb); UV_EXTERN int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, @@ -595,7 +611,17 @@ enum uv_udp_flags { * (provided they all set the flag) but only the last one to bind will receive * any traffic, in effect "stealing" the port from the previous listener. */ - UV_UDP_REUSEADDR = 4 + UV_UDP_REUSEADDR = 4, + /* + * Indicates that the message was received by recvmmsg, so the buffer provided + * must not be freed by the recv_cb callback. + */ + UV_UDP_MMSG_CHUNK = 8, + + /* + * Indicates that recvmmsg should be used, if available. + */ + UV_UDP_RECVMMSG = 256 }; typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); @@ -647,6 +673,11 @@ UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership); +UV_EXTERN int uv_udp_set_source_membership(uv_udp_t* handle, + const char* multicast_addr, + const char* interface_addr, + const char* source_addr, + uv_membership membership); UV_EXTERN int uv_udp_set_multicast_loop(uv_udp_t* handle, int on); UV_EXTERN int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl); UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle, @@ -691,10 +722,25 @@ typedef enum { UV_TTY_MODE_IO } uv_tty_mode_t; +typedef enum { + /* + * The console supports handling of virtual terminal sequences + * (Windows10 new console, ConEmu) + */ + UV_TTY_SUPPORTED, + /* The console cannot process the virtual terminal sequence. (Legacy + * console) + */ + UV_TTY_UNSUPPORTED +} uv_tty_vtermstate_t; + + UV_EXTERN int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int readable); UV_EXTERN int uv_tty_set_mode(uv_tty_t*, uv_tty_mode_t mode); UV_EXTERN int uv_tty_reset_mode(void); UV_EXTERN int uv_tty_get_winsize(uv_tty_t*, int* width, int* height); +UV_EXTERN void uv_tty_set_vterm_state(uv_tty_vtermstate_t state); +UV_EXTERN int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state); #ifdef __cplusplus extern "C++" { @@ -1042,11 +1088,11 @@ UV_EXTERN int uv_cancel(uv_req_t* req); struct uv_cpu_times_s { - uint64_t user; - uint64_t nice; - uint64_t sys; - uint64_t idle; - uint64_t irq; + uint64_t user; /* milliseconds */ + uint64_t nice; /* milliseconds */ + uint64_t sys; /* milliseconds */ + uint64_t idle; /* milliseconds */ + uint64_t irq; /* milliseconds */ }; struct uv_cpu_info_s { @@ -1087,6 +1133,17 @@ struct uv_utsname_s { to as meaningless in the docs. */ }; +struct uv_statfs_s { + uint64_t f_type; + uint64_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + uint64_t f_spare[4]; +}; + typedef enum { UV_DIRENT_UNKNOWN, UV_DIRENT_FILE, @@ -1149,12 +1206,22 @@ UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd); UV_EXTERN uv_pid_t uv_os_getpid(void); UV_EXTERN uv_pid_t uv_os_getppid(void); -#define UV_PRIORITY_LOW 19 -#define UV_PRIORITY_BELOW_NORMAL 10 -#define UV_PRIORITY_NORMAL 0 -#define UV_PRIORITY_ABOVE_NORMAL -7 -#define UV_PRIORITY_HIGH -14 -#define UV_PRIORITY_HIGHEST -20 +#if defined(__PASE__) +/* On IBM i PASE, the highest process priority is -10 */ +# define UV_PRIORITY_LOW 39 // RUNPTY(99) +# define UV_PRIORITY_BELOW_NORMAL 15 // RUNPTY(50) +# define UV_PRIORITY_NORMAL 0 // RUNPTY(20) +# define UV_PRIORITY_ABOVE_NORMAL -4 // RUNTY(12) +# define UV_PRIORITY_HIGH -7 // RUNPTY(6) +# define UV_PRIORITY_HIGHEST -10 // RUNPTY(1) +#else +# define UV_PRIORITY_LOW 19 +# define UV_PRIORITY_BELOW_NORMAL 10 +# define UV_PRIORITY_NORMAL 0 +# define UV_PRIORITY_ABOVE_NORMAL -7 +# define UV_PRIORITY_HIGH -14 +# define UV_PRIORITY_HIGHEST -20 +#endif UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority); UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority); @@ -1168,6 +1235,13 @@ UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses, UV_EXTERN void uv_free_interface_addresses(uv_interface_address_t* addresses, int count); +struct uv_env_item_s { + char* name; + char* value; +}; + +UV_EXTERN int uv_os_environ(uv_env_item_t** envitems, int* count); +UV_EXTERN void uv_os_free_environ(uv_env_item_t* envitems, int count); UV_EXTERN int uv_os_getenv(const char* name, char* buffer, size_t* size); UV_EXTERN int uv_os_setenv(const char* name, const char* value); UV_EXTERN int uv_os_unsetenv(const char* name); @@ -1223,7 +1297,10 @@ typedef enum { UV_FS_LCHOWN, UV_FS_OPENDIR, UV_FS_READDIR, - UV_FS_CLOSEDIR + UV_FS_CLOSEDIR, + UV_FS_STATFS, + UV_FS_MKSTEMP, + UV_FS_LUTIME } uv_fs_type; struct uv_dir_s { @@ -1248,6 +1325,7 @@ struct uv_fs_s { UV_EXTERN uv_fs_type uv_fs_get_type(const uv_fs_t*); UV_EXTERN ssize_t uv_fs_get_result(const uv_fs_t*); +UV_EXTERN int uv_fs_get_system_error(const uv_fs_t*); UV_EXTERN void* uv_fs_get_ptr(const uv_fs_t*); UV_EXTERN const char* uv_fs_get_path(const uv_fs_t*); UV_EXTERN uv_stat_t* uv_fs_get_statbuf(uv_fs_t*); @@ -1314,6 +1392,10 @@ UV_EXTERN int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb); +UV_EXTERN int uv_fs_mkstemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb); UV_EXTERN int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1392,6 +1474,12 @@ UV_EXTERN int uv_fs_futime(uv_loop_t* loop, double atime, double mtime, uv_fs_cb cb); +UV_EXTERN int uv_fs_lutime(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + double atime, + double mtime, + uv_fs_cb cb); UV_EXTERN int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1451,6 +1539,10 @@ UV_EXTERN int uv_fs_lchown(uv_loop_t* loop, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); +UV_EXTERN int uv_fs_statfs(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb); enum uv_fs_event { @@ -1556,6 +1648,26 @@ UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size); UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst); + +struct uv_random_s { + UV_REQ_FIELDS + /* read-only */ + uv_loop_t* loop; + /* private */ + int status; + void* buf; + size_t buflen; + uv_random_cb cb; + struct uv__work work_req; +}; + +UV_EXTERN int uv_random(uv_loop_t* loop, + uv_random_t* req, + void *buf, + size_t buflen, + unsigned flags, /* For future extension; must be 0. */ + uv_random_cb cb); + #if defined(IF_NAMESIZE) # define UV_IF_NAMESIZE (IF_NAMESIZE + 1) #elif defined(IFNAMSIZ) @@ -1582,6 +1694,7 @@ UV_EXTERN uint64_t uv_get_total_memory(void); UV_EXTERN uint64_t uv_get_constrained_memory(void); UV_EXTERN uint64_t uv_hrtime(void); +UV_EXTERN void uv_sleep(unsigned int msec); UV_EXTERN void uv_disable_stdio_inheritance(void); diff --git a/Utilities/cmlibuv/include/uv/errno.h b/Utilities/cmlibuv/include/uv/errno.h index 8eeb95de31b..165fd11c376 100644 --- a/Utilities/cmlibuv/include/uv/errno.h +++ b/Utilities/cmlibuv/include/uv/errno.h @@ -439,5 +439,10 @@ # define UV__EFTYPE (-4028) #endif +#if defined(EILSEQ) && !defined(_WIN32) +# define UV__EILSEQ UV__ERR(EILSEQ) +#else +# define UV__EILSEQ (-4027) +#endif #endif /* UV_ERRNO_H_ */ diff --git a/Utilities/cmlibuv/include/uv/unix.h b/Utilities/cmlibuv/include/uv/unix.h index 4e261081bbb..82778ffb9e9 100644 --- a/Utilities/cmlibuv/include/uv/unix.h +++ b/Utilities/cmlibuv/include/uv/unix.h @@ -51,6 +51,8 @@ # include "linux.h" #elif defined (__MVS__) # include "os390.h" +#elif defined(__PASE__) /* __PASE__ and _AIX are both defined on IBM i */ +# include "posix.h" /* IBM i needs uv/posix.h, not uv/aix.h */ #elif defined(_AIX) # include "aix.h" #elif defined(__sun) @@ -65,8 +67,7 @@ defined(__OpenBSD__) || \ defined(__NetBSD__) # include "bsd.h" -#elif defined(__PASE__) || \ - defined(__CYGWIN__) || \ +#elif defined(__CYGWIN__) || \ defined(__MSYS__) || \ defined(__GNU__) # include "posix.h" @@ -421,11 +422,25 @@ typedef struct { #else # define UV_FS_O_CREAT 0 #endif -#if defined(O_DIRECT) + +#if defined(__linux__) && defined(__arm__) +# define UV_FS_O_DIRECT 0x10000 +#elif defined(__linux__) && defined(__m68k__) +# define UV_FS_O_DIRECT 0x10000 +#elif defined(__linux__) && defined(__mips__) +# define UV_FS_O_DIRECT 0x08000 +#elif defined(__linux__) && defined(__powerpc__) +# define UV_FS_O_DIRECT 0x20000 +#elif defined(__linux__) && defined(__s390x__) +# define UV_FS_O_DIRECT 0x04000 +#elif defined(__linux__) && defined(__x86_64__) +# define UV_FS_O_DIRECT 0x04000 +#elif defined(O_DIRECT) # define UV_FS_O_DIRECT O_DIRECT #else # define UV_FS_O_DIRECT 0 #endif + #if defined(O_DIRECTORY) # define UV_FS_O_DIRECTORY O_DIRECTORY #else @@ -498,6 +513,7 @@ typedef struct { #endif /* fs open() flags supported on other platforms: */ +#define UV_FS_O_FILEMAP 0 #define UV_FS_O_RANDOM 0 #define UV_FS_O_SHORT_LIVED 0 #define UV_FS_O_SEQUENTIAL 0 diff --git a/Utilities/cmlibuv/include/uv/version.h b/Utilities/cmlibuv/include/uv/version.h index 97f0bc2459b..f932483e4e7 100644 --- a/Utilities/cmlibuv/include/uv/version.h +++ b/Utilities/cmlibuv/include/uv/version.h @@ -26,13 +26,13 @@ * Versions with the same major number are ABI stable. API is allowed to * evolve between minor releases, but only in a backwards compatible way. * Make sure you update the -soname directives in configure.ac - * and uv.gyp whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but + * whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but * not UV_VERSION_PATCH.) */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 29 -#define UV_VERSION_PATCH 2 +#define UV_VERSION_MINOR 37 +#define UV_VERSION_PATCH 1 #define UV_VERSION_IS_RELEASE 0 #define UV_VERSION_SUFFIX "dev" diff --git a/Utilities/cmlibuv/include/uv/win.h b/Utilities/cmlibuv/include/uv/win.h index 7f77cc23864..f86357b4edf 100644 --- a/Utilities/cmlibuv/include/uv/win.h +++ b/Utilities/cmlibuv/include/uv/win.h @@ -528,7 +528,7 @@ typedef struct { /* eol conversion state */ \ unsigned char previous_eol; \ /* ansi parser state */ \ - unsigned char ansi_parser_state; \ + unsigned short ansi_parser_state; \ unsigned char ansi_csi_argc; \ unsigned short ansi_csi_argv[4]; \ COORD saved_position; \ @@ -679,6 +679,7 @@ typedef struct { #define UV_FS_O_APPEND _O_APPEND #define UV_FS_O_CREAT _O_CREAT #define UV_FS_O_EXCL _O_EXCL +#define UV_FS_O_FILEMAP 0x20000000 #define UV_FS_O_RANDOM _O_RANDOM #define UV_FS_O_RDONLY _O_RDONLY #define UV_FS_O_RDWR _O_RDWR diff --git a/Utilities/cmlibuv/src/random.c b/Utilities/cmlibuv/src/random.c new file mode 100644 index 00000000000..491bf703309 --- /dev/null +++ b/Utilities/cmlibuv/src/random.c @@ -0,0 +1,123 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "uv-common.h" + +#ifdef _WIN32 +# include "win/internal.h" +#else +# include "unix/internal.h" +#endif + +static int uv__random(void* buf, size_t buflen) { + int rc; + +#if defined(__PASE__) + rc = uv__random_readpath("/dev/urandom", buf, buflen); +#elif defined(_AIX) + rc = uv__random_readpath("/dev/random", buf, buflen); +#elif defined(__APPLE__) || defined(__OpenBSD__) || \ + (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) + rc = uv__random_getentropy(buf, buflen); + if (rc == UV_ENOSYS) + rc = uv__random_devurandom(buf, buflen); +#elif defined(__NetBSD__) + rc = uv__random_sysctl(buf, buflen); +#elif defined(__FreeBSD__) || defined(__linux__) + rc = uv__random_getrandom(buf, buflen); + if (rc == UV_ENOSYS) + rc = uv__random_devurandom(buf, buflen); +# if defined(__linux__) + switch (rc) { + case UV_EACCES: + case UV_EIO: + case UV_ELOOP: + case UV_EMFILE: + case UV_ENFILE: + case UV_ENOENT: + case UV_EPERM: + rc = uv__random_sysctl(buf, buflen); + break; + } +# endif +#elif defined(_WIN32) + uv__once_init(); + rc = uv__random_rtlgenrandom(buf, buflen); +#else + rc = uv__random_devurandom(buf, buflen); +#endif + + return rc; +} + + +static void uv__random_work(struct uv__work* w) { + uv_random_t* req; + + req = container_of(w, uv_random_t, work_req); + req->status = uv__random(req->buf, req->buflen); +} + + +static void uv__random_done(struct uv__work* w, int status) { + uv_random_t* req; + + req = container_of(w, uv_random_t, work_req); + uv__req_unregister(req->loop, req); + + if (status == 0) + status = req->status; + + req->cb(req, status, req->buf, req->buflen); +} + + +int uv_random(uv_loop_t* loop, + uv_random_t* req, + void *buf, + size_t buflen, + unsigned flags, + uv_random_cb cb) { + if (buflen > 0x7FFFFFFFu) + return UV_E2BIG; + + if (flags != 0) + return UV_EINVAL; + + if (cb == NULL) + return uv__random(buf, buflen); + + uv__req_init(loop, req, UV_RANDOM); + req->loop = loop; + req->status = 0; + req->cb = cb; + req->buf = buf; + req->buflen = buflen; + + uv__work_submit(loop, + &req->work_req, + UV__WORK_CPU, + uv__random_work, + uv__random_done); + + return 0; +} diff --git a/Utilities/cmlibuv/src/threadpool.c b/Utilities/cmlibuv/src/threadpool.c index 7aa575508fa..0998938f3e0 100644 --- a/Utilities/cmlibuv/src/threadpool.c +++ b/Utilities/cmlibuv/src/threadpool.c @@ -160,8 +160,8 @@ static void post(QUEUE* q, enum uv__work_kind kind) { } +void uv__threadpool_cleanup(void) { #ifndef _WIN32 -UV_DESTRUCTOR(static void cleanup(void)) { unsigned int i; if (nthreads == 0) @@ -181,8 +181,8 @@ UV_DESTRUCTOR(static void cleanup(void)) { threads = NULL; nthreads = 0; -} #endif +} static void init_threads(void) { @@ -372,6 +372,10 @@ int uv_cancel(uv_req_t* req) { loop = ((uv_getnameinfo_t*) req)->loop; wreq = &((uv_getnameinfo_t*) req)->work_req; break; + case UV_RANDOM: + loop = ((uv_random_t*) req)->loop; + wreq = &((uv_random_t*) req)->work_req; + break; case UV_WORK: loop = ((uv_work_t*) req)->loop; wreq = &((uv_work_t*) req)->work_req; diff --git a/Utilities/cmlibuv/src/timer.c b/Utilities/cmlibuv/src/timer.c index dd78bcbad9a..4cf4ed42648 100644 --- a/Utilities/cmlibuv/src/timer.c +++ b/Utilities/cmlibuv/src/timer.c @@ -51,12 +51,7 @@ static int timer_less_than(const struct heap_node* ha, /* Compare start_id when both have the same timeout. start_id is * allocated with loop->timer_counter in uv_timer_start(). */ - if (a->start_id < b->start_id) - return 1; - if (b->start_id < a->start_id) - return 0; - - return 0; + return a->start_id < b->start_id; } @@ -74,7 +69,7 @@ int uv_timer_start(uv_timer_t* handle, uint64_t repeat) { uint64_t clamped_timeout; - if (cb == NULL) + if (uv__is_closing(handle) || cb == NULL) return UV_EINVAL; if (uv__is_active(handle)) @@ -87,7 +82,7 @@ int uv_timer_start(uv_timer_t* handle, handle->timer_cb = cb; handle->timeout = clamped_timeout; handle->repeat = repeat; - /* start_id is the second index to be compared in uv__timer_cmp() */ + /* start_id is the second index to be compared in timer_less_than() */ handle->start_id = handle->loop->timer_counter++; heap_insert(timer_heap(handle->loop), diff --git a/Utilities/cmlibuv/src/unix/aix-common.c b/Utilities/cmlibuv/src/unix/aix-common.c index e9697e9c275..44c87b10a0b 100644 --- a/Utilities/cmlibuv/src/unix/aix-common.c +++ b/Utilities/cmlibuv/src/unix/aix-common.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -155,144 +156,3 @@ int uv_exepath(char* buffer, size_t* size) { } } -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; - - for (i = 0; i < count; ++i) { - uv__free(cpu_infos[i].model); - } - - uv__free(cpu_infos); -} - - -int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { - uv_interface_address_t* address; - int sockfd, inet6, size = 1; - struct ifconf ifc; - struct ifreq *ifr, *p, flg; - struct sockaddr_dl* sa_addr; - - *count = 0; - *addresses = NULL; - - if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) { - return UV__ERR(errno); - } - - if (ioctl(sockfd, SIOCGSIZIFCONF, &size) == -1) { - uv__close(sockfd); - return UV__ERR(errno); - } - - ifc.ifc_req = (struct ifreq*)uv__malloc(size); - ifc.ifc_len = size; - if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { - uv__close(sockfd); - return UV__ERR(errno); - } - -#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) - - /* Count all up and running ipv4/ipv6 addresses */ - ifr = ifc.ifc_req; - while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { - p = ifr; - ifr = (struct ifreq*) - ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); - - if (!(p->ifr_addr.sa_family == AF_INET6 || - p->ifr_addr.sa_family == AF_INET)) - continue; - - memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); - if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { - uv__close(sockfd); - return UV__ERR(errno); - } - - if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) - continue; - - (*count)++; - } - - if (*count == 0) { - uv__close(sockfd); - return 0; - } - - /* Alloc the return interface structs */ - *addresses = uv__malloc(*count * sizeof(uv_interface_address_t)); - if (!(*addresses)) { - uv__close(sockfd); - return UV_ENOMEM; - } - address = *addresses; - - ifr = ifc.ifc_req; - while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { - p = ifr; - ifr = (struct ifreq*) - ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); - - if (!(p->ifr_addr.sa_family == AF_INET6 || - p->ifr_addr.sa_family == AF_INET)) - continue; - - inet6 = (p->ifr_addr.sa_family == AF_INET6); - - memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); - if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { - uv__close(sockfd); - return UV_ENOSYS; - } - - if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) - continue; - - /* All conditions above must match count loop */ - - address->name = uv__strdup(p->ifr_name); - - if (inet6) - address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); - else - address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); - - sa_addr = (struct sockaddr_dl*) &p->ifr_addr; - memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); - - if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) { - uv__close(sockfd); - return UV_ENOSYS; - } - - if (inet6) - address->netmask.netmask6 = *((struct sockaddr_in6*) &p->ifr_addr); - else - address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr); - - address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; - - address++; - } - -#undef ADDR_SIZE - - uv__close(sockfd); - return 0; -} - - -void uv_free_interface_addresses(uv_interface_address_t* addresses, - int count) { - int i; - - for (i = 0; i < count; ++i) { - uv__free(addresses[i].name); - } - - uv__free(addresses); -} - diff --git a/Utilities/cmlibuv/src/unix/aix.c b/Utilities/cmlibuv/src/unix/aix.c index 1f36926c02e..6b4594b43e9 100644 --- a/Utilities/cmlibuv/src/unix/aix.c +++ b/Utilities/cmlibuv/src/unix/aix.c @@ -926,7 +926,7 @@ int uv_get_process_title(char* buffer, size_t size) { } -UV_DESTRUCTOR(static void free_args_mem(void)) { +void uv__process_title_cleanup(void) { uv__free(args_mem); /* Keep valgrind happy. */ args_mem = NULL; } @@ -1039,6 +1039,186 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + uv_interface_address_t* address; + int sockfd, sock6fd, inet6, i, r, size = 1; + struct ifconf ifc; + struct ifreq *ifr, *p, flg; + struct in6_ifreq if6; + struct sockaddr_dl* sa_addr; + + ifc.ifc_req = NULL; + sock6fd = -1; + r = 0; + *count = 0; + *addresses = NULL; + + if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) { + r = UV__ERR(errno); + goto cleanup; + } + + if (0 > (sock6fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP))) { + r = UV__ERR(errno); + goto cleanup; + } + + if (ioctl(sockfd, SIOCGSIZIFCONF, &size) == -1) { + r = UV__ERR(errno); + goto cleanup; + } + + ifc.ifc_req = (struct ifreq*)uv__malloc(size); + if (ifc.ifc_req == NULL) { + r = UV_ENOMEM; + goto cleanup; + } + ifc.ifc_len = size; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { + r = UV__ERR(errno); + goto cleanup; + } + +#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) + + /* Count all up and running ipv4/ipv6 addresses */ + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + r = UV__ERR(errno); + goto cleanup; + } + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + (*count)++; + } + + if (*count == 0) + goto cleanup; + + /* Alloc the return interface structs */ + *addresses = uv__calloc(*count, sizeof(**addresses)); + if (!(*addresses)) { + r = UV_ENOMEM; + goto cleanup; + } + address = *addresses; + + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + inet6 = (p->ifr_addr.sa_family == AF_INET6); + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) + goto syserror; + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + /* All conditions above must match count loop */ + + address->name = uv__strdup(p->ifr_name); + + if (inet6) + address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); + else + address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); + + if (inet6) { + memset(&if6, 0, sizeof(if6)); + r = uv__strscpy(if6.ifr_name, p->ifr_name, sizeof(if6.ifr_name)); + if (r == UV_E2BIG) + goto cleanup; + r = 0; + memcpy(&if6.ifr_Addr, &p->ifr_addr, sizeof(if6.ifr_Addr)); + if (ioctl(sock6fd, SIOCGIFNETMASK6, &if6) == -1) + goto syserror; + address->netmask.netmask6 = *((struct sockaddr_in6*) &if6.ifr_Addr); + /* Explicitly set family as the ioctl call appears to return it as 0. */ + address->netmask.netmask6.sin6_family = AF_INET6; + } else { + if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) + goto syserror; + address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr); + /* Explicitly set family as the ioctl call appears to return it as 0. */ + address->netmask.netmask4.sin_family = AF_INET; + } + + address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; + + address++; + } + + /* Fill in physical addresses. */ + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (p->ifr_addr.sa_family != AF_LINK) + continue; + + address = *addresses; + for (i = 0; i < *count; i++) { + if (strcmp(address->name, p->ifr_name) == 0) { + sa_addr = (struct sockaddr_dl*) &p->ifr_addr; + memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); + } + address++; + } + } + +#undef ADDR_SIZE + goto cleanup; + +syserror: + uv_free_interface_addresses(*addresses, *count); + *addresses = NULL; + *count = 0; + r = UV_ENOSYS; + +cleanup: + if (sockfd != -1) + uv__close(sockfd); + if (sock6fd != -1) + uv__close(sock6fd); + uv__free(ifc.ifc_req); + return r; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; ++i) { + uv__free(addresses[i].name); + } + + uv__free(addresses); +} + + void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct pollfd* events; uintptr_t i; diff --git a/Utilities/cmlibuv/src/unix/android-ifaddrs.c b/Utilities/cmlibuv/src/unix/android-ifaddrs.c index 99fb25a43b4..4765cc06b57 100644 --- a/Utilities/cmlibuv/src/unix/android-ifaddrs.c +++ b/Utilities/cmlibuv/src/unix/android-ifaddrs.c @@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include typedef struct NetlinkList { @@ -469,12 +470,14 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, { case IFA_ADDRESS: case IFA_LOCAL: + l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask) { /* Make room for netmask */ l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); l_addedNetmask = 1; } + break; case IFA_BROADCAST: l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); break; diff --git a/Utilities/cmlibuv/src/unix/async.c b/Utilities/cmlibuv/src/unix/async.c index a5c47bca059..5f58fb88d62 100644 --- a/Utilities/cmlibuv/src/unix/async.c +++ b/Utilities/cmlibuv/src/unix/async.c @@ -32,10 +32,14 @@ #include #include #include +#include /* sched_yield() */ + +#ifdef __linux__ +#include +#endif static void uv__async_send(uv_loop_t* loop); static int uv__async_start(uv_loop_t* loop); -static int uv__async_eventfd(void); int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { @@ -78,20 +82,32 @@ int uv_async_send(uv_async_t* handle) { /* Only call this from the event loop thread. */ static int uv__async_spin(uv_async_t* handle) { + int i; int rc; for (;;) { - /* rc=0 -- handle is not pending. - * rc=1 -- handle is pending, other thread is still working with it. - * rc=2 -- handle is pending, other thread is done. + /* 997 is not completely chosen at random. It's a prime number, acyclical + * by nature, and should therefore hopefully dampen sympathetic resonance. */ - rc = cmpxchgi(&handle->pending, 2, 0); - - if (rc != 1) - return rc; + for (i = 0; i < 997; i++) { + /* rc=0 -- handle is not pending. + * rc=1 -- handle is pending, other thread is still working with it. + * rc=2 -- handle is pending, other thread is done. + */ + rc = cmpxchgi(&handle->pending, 2, 0); + + if (rc != 1) + return rc; + + /* Other thread is busy with this handle, spin until it's done. */ + cpu_relax(); + } - /* Other thread is busy with this handle, spin until it's done. */ - cpu_relax(); + /* Yield the CPU. We may have preempted the other thread while it's + * inside the critical section and if it's running on the same CPU + * as us, we'll just burn CPU cycles until the end of our time slice. + */ + sched_yield(); } } @@ -190,36 +206,18 @@ static int uv__async_start(uv_loop_t* loop) { if (loop->async_io_watcher.fd != -1) return 0; - err = uv__async_eventfd(); - if (err >= 0) { - pipefd[0] = err; - pipefd[1] = -1; - } - else if (err == UV_ENOSYS) { - err = uv__make_pipe(pipefd, UV__F_NONBLOCK); -#if defined(__linux__) - /* Save a file descriptor by opening one of the pipe descriptors as - * read/write through the procfs. That file descriptor can then - * function as both ends of the pipe. - */ - if (err == 0) { - char buf[32]; - int fd; - - snprintf(buf, sizeof(buf), "/proc/self/fd/%d", pipefd[0]); - fd = uv__open_cloexec(buf, O_RDWR); - if (fd >= 0) { - uv__close(pipefd[0]); - uv__close(pipefd[1]); - pipefd[0] = fd; - pipefd[1] = fd; - } - } -#endif - } +#ifdef __linux__ + err = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (err < 0) + return UV__ERR(errno); + pipefd[0] = err; + pipefd[1] = -1; +#else + err = uv__make_pipe(pipefd, UV__F_NONBLOCK); if (err < 0) return err; +#endif uv__io_init(&loop->async_io_watcher, uv__async_io, pipefd[0]); uv__io_start(loop, &loop->async_io_watcher, POLLIN); @@ -253,46 +251,3 @@ void uv__async_stop(uv_loop_t* loop) { uv__close(loop->async_io_watcher.fd); loop->async_io_watcher.fd = -1; } - - -static int uv__async_eventfd(void) { -#if defined(__linux__) - static int no_eventfd2; - static int no_eventfd; - int fd; - - if (no_eventfd2) - goto skip_eventfd2; - - fd = uv__eventfd2(0, UV__EFD_CLOEXEC | UV__EFD_NONBLOCK); - if (fd != -1) - return fd; - - if (errno != ENOSYS) - return UV__ERR(errno); - - no_eventfd2 = 1; - -skip_eventfd2: - - if (no_eventfd) - goto skip_eventfd; - - fd = uv__eventfd(0); - if (fd != -1) { - uv__cloexec(fd, 1); - uv__nonblock(fd, 1); - return fd; - } - - if (errno != ENOSYS) - return UV__ERR(errno); - - no_eventfd = 1; - -skip_eventfd: - -#endif - - return UV_ENOSYS; -} diff --git a/Utilities/cmlibuv/src/unix/atomic-ops.h b/Utilities/cmlibuv/src/unix/atomic-ops.h index 995aca63ac8..2518a0680fb 100644 --- a/Utilities/cmlibuv/src/unix/atomic-ops.h +++ b/Utilities/cmlibuv/src/unix/atomic-ops.h @@ -36,10 +36,12 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { : "r" (newval), "0" (oldval) : "memory"); return out; -#elif defined(_AIX) && (defined(__xlC__) || defined(__ibmxl__)) - const int out = (*(volatile int*) ptr); - __compare_and_swap(ptr, &oldval, newval); - return out; +#elif defined(_AIX) && defined(__ibmxl__) + /* FIXME: This is not actually atomic but XLClang 16.1 for AIX + does not provide __sync_val_compare_and_swap or an equivalent. + Its documentation suggests using C++11 atomics but this is C. */ + __compare_and_swap((volatile int*)ptr, &oldval, newval); + return oldval; #elif defined(__MVS__) unsigned int op4; if (__plo_CSST(ptr, (unsigned int*) &oldval, newval, @@ -57,6 +59,8 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { UV_UNUSED(static void cpu_relax(void)) { #if defined(__i386__) || defined(__x86_64__) __asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */ +#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) + __asm__ volatile("yield"); #endif } diff --git a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c index 0d7bbe662a5..a3385af17c8 100644 --- a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c +++ b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c @@ -69,7 +69,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { struct ifaddrs* addrs; struct ifaddrs* ent; uv_interface_address_t* address; +#if !(defined(__CYGWIN__) || defined(__MSYS__)) int i; +#endif *count = 0; *addresses = NULL; diff --git a/Utilities/cmlibuv/src/unix/bsd-proctitle.c b/Utilities/cmlibuv/src/unix/bsd-proctitle.c index 0ce47c8f64e..723b81c01c2 100644 --- a/Utilities/cmlibuv/src/unix/bsd-proctitle.c +++ b/Utilities/cmlibuv/src/unix/bsd-proctitle.c @@ -37,6 +37,13 @@ static void init_process_title_mutex_once(void) { } +void uv__process_title_cleanup(void) { + /* TODO(bnoordhuis) uv_mutex_destroy(&process_title_mutex) + * and reset process_title_mutex_once? + */ +} + + char** uv_setup_args(int argc, char** argv) { process_title = argc > 0 ? uv__strdup(argv[0]) : NULL; return argv; diff --git a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c index d42ff05c562..6cf42fabd2c 100644 --- a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c +++ b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c @@ -1,6 +1,12 @@ #include "uv.h" #include "internal.h" +void uv__process_title_cleanup(void) { +} + +void uv__threadpool_cleanup(void) { +} + int uv__tcp_nodelay(int fd, int on) { errno = EINVAL; return -1; diff --git a/Utilities/cmlibuv/src/unix/core.c b/Utilities/cmlibuv/src/unix/core.c index cf7dea050b9..7b80ed50f3f 100644 --- a/Utilities/cmlibuv/src/unix/core.c +++ b/Utilities/cmlibuv/src/unix/core.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include /* O_CLOEXEC */ #include #include #include @@ -50,37 +50,36 @@ # include #endif -#ifdef __APPLE__ -# include /* _NSGetExecutablePath */ +#if defined(__APPLE__) # include -# if defined(O_CLOEXEC) -# define UV__O_CLOEXEC O_CLOEXEC -# endif -#endif +# endif /* defined(__APPLE__) */ + + +#if defined(__APPLE__) && !TARGET_OS_IPHONE +# include +# include /* _NSGetExecutablePath */ +# define environ (*_NSGetEnviron()) +#else /* defined(__APPLE__) && !TARGET_OS_IPHONE */ +extern char** environ; +#endif /* !(defined(__APPLE__) && !TARGET_OS_IPHONE) */ + #if defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__FreeBSD_kernel__) || \ - defined(__NetBSD__) + defined(__NetBSD__) || \ + defined(__OpenBSD__) # include # include # include # include # include -# define UV__O_CLOEXEC O_CLOEXEC -# if defined(__FreeBSD__) && __FreeBSD__ >= 10 +# if defined(__FreeBSD__) # define uv__accept4 accept4 # endif # if defined(__NetBSD__) # define uv__accept4(a, b, c, d) paccept((a), (b), (c), NULL, (d)) # endif -# if (defined(__FreeBSD__) && __FreeBSD__ >= 10) || defined(__NetBSD__) -# define UV__SOCK_NONBLOCK SOCK_NONBLOCK -# define UV__SOCK_CLOEXEC SOCK_CLOEXEC -# endif -# if !defined(F_DUP2FD_CLOEXEC) && defined(_F_DUP2FD_CLOEXEC) -# define F_DUP2FD_CLOEXEC _F_DUP2FD_CLOEXEC -# endif #endif #if defined(__ANDROID_API__) && __ANDROID_API__ < 21 @@ -92,7 +91,8 @@ #endif #if defined(__linux__) -#include +# include +# define uv__accept4 accept4 #endif static int uv__run_pending(uv_loop_t* loop); @@ -175,9 +175,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { case UV_SIGNAL: uv__signal_close((uv_signal_t*) handle); - /* Signal handles may not be closed immediately. The signal code will - * itself close uv__make_close_pending whenever appropriate. */ - return; + break; default: assert(0); @@ -242,6 +240,8 @@ int uv__getiovmax(void) { static void uv__finish_close(uv_handle_t* handle) { + uv_signal_t* sh; + /* Note: while the handle is in the UV_HANDLE_CLOSING state now, it's still * possible for it to be active in the sense that uv__is_active() returns * true. @@ -264,7 +264,20 @@ static void uv__finish_close(uv_handle_t* handle) { case UV_FS_EVENT: case UV_FS_POLL: case UV_POLL: + break; + case UV_SIGNAL: + /* If there are any caught signals "trapped" in the signal pipe, + * we can't call the close callback yet. Reinserting the handle + * into the closing queue makes the event loop spin but that's + * okay because we only need to deliver the pending events. + */ + sh = (uv_signal_t*) handle; + if (sh->caught_signals > sh->dispatched_signals) { + handle->flags ^= UV_HANDLE_CLOSED; + uv__make_close_pending(handle); /* Back into the queue. */ + return; + } break; case UV_NAMED_PIPE: @@ -468,52 +481,32 @@ int uv__accept(int sockfd) { int peerfd; int err; + (void) &err; assert(sockfd >= 0); - while (1) { -#if defined(__linux__) || \ - (defined(__FreeBSD__) && __FreeBSD__ >= 10) || \ - defined(__NetBSD__) - static int no_accept4; - - if (no_accept4) - goto skip; - - peerfd = uv__accept4(sockfd, - NULL, - NULL, - UV__SOCK_NONBLOCK|UV__SOCK_CLOEXEC); - if (peerfd != -1) - return peerfd; - - if (errno == EINTR) - continue; - - if (errno != ENOSYS) - return UV__ERR(errno); - - no_accept4 = 1; -skip: -#endif - + do +#ifdef uv__accept4 + peerfd = uv__accept4(sockfd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); +#else peerfd = accept(sockfd, NULL, NULL); - if (peerfd == -1) { - if (errno == EINTR) - continue; - return UV__ERR(errno); - } +#endif + while (peerfd == -1 && errno == EINTR); - err = uv__cloexec(peerfd, 1); - if (err == 0) - err = uv__nonblock(peerfd, 1); + if (peerfd == -1) + return UV__ERR(errno); - if (err) { - uv__close(peerfd); - return err; - } +#ifndef uv__accept4 + err = uv__cloexec(peerfd, 1); + if (err == 0) + err = uv__nonblock(peerfd, 1); - return peerfd; + if (err != 0) { + uv__close(peerfd); + return err; } +#endif + + return peerfd; } @@ -529,7 +522,7 @@ int uv__close_nocancel(int fd) { #if defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" -#if defined(__LP64__) +#if defined(__LP64__) || TARGET_OS_IPHONE extern int close$NOCANCEL(int); return close$NOCANCEL(fd); #else @@ -704,16 +697,38 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { int uv_cwd(char* buffer, size_t* size) { + char scratch[1 + UV__PATH_MAX]; + if (buffer == NULL || size == NULL) return UV_EINVAL; - if (getcwd(buffer, *size) == NULL) + /* Try to read directly into the user's buffer first... */ + if (getcwd(buffer, *size) != NULL) + goto fixup; + + if (errno != ERANGE) + return UV__ERR(errno); + + /* ...or into scratch space if the user's buffer is too small + * so we can report how much space to provide on the next try. + */ + if (getcwd(scratch, sizeof(scratch)) == NULL) return UV__ERR(errno); + buffer = scratch; + +fixup: + *size = strlen(buffer); + if (*size > 1 && buffer[*size - 1] == '/') { - buffer[*size-1] = '\0'; - (*size)--; + *size -= 1; + buffer[*size] = '\0'; + } + + if (buffer == scratch) { + *size += 1; + return UV_ENOBUFS; } return 0; @@ -823,8 +838,8 @@ static void maybe_resize(uv_loop_t* loop, unsigned int len) { } nwatchers = next_power_of_two(len + 2) - 2; - watchers = uv__realloc(loop->watchers, - (nwatchers + 2) * sizeof(loop->watchers[0])); + watchers = uv__reallocf(loop->watchers, + (nwatchers + 2) * sizeof(loop->watchers[0])); if (watchers == NULL) abort(); @@ -977,24 +992,17 @@ int uv_getrusage(uv_rusage_t* rusage) { int uv__open_cloexec(const char* path, int flags) { - int err; +#if defined(O_CLOEXEC) int fd; -#if defined(UV__O_CLOEXEC) - static int no_cloexec; - - if (!no_cloexec) { - fd = open(path, flags | UV__O_CLOEXEC); - if (fd != -1) - return fd; - - if (errno != EINVAL) - return UV__ERR(errno); + fd = open(path, flags | O_CLOEXEC); + if (fd == -1) + return UV__ERR(errno); - /* O_CLOEXEC not supported. */ - no_cloexec = 1; - } -#endif + return fd; +#else /* O_CLOEXEC */ + int err; + int fd; fd = open(path, flags); if (fd == -1) @@ -1007,58 +1015,35 @@ int uv__open_cloexec(const char* path, int flags) { } return fd; +#endif /* O_CLOEXEC */ } int uv__dup2_cloexec(int oldfd, int newfd) { +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__linux__) int r; -#if (defined(__FreeBSD__) && __FreeBSD__ >= 10) || defined(__NetBSD__) + r = dup3(oldfd, newfd, O_CLOEXEC); if (r == -1) return UV__ERR(errno); + return r; -#elif defined(__FreeBSD__) && defined(F_DUP2FD_CLOEXEC) - r = fcntl(oldfd, F_DUP2FD_CLOEXEC, newfd); - if (r != -1) - return r; - if (errno != EINVAL) - return UV__ERR(errno); - /* Fall through. */ -#elif defined(__linux__) - static int no_dup3; - if (!no_dup3) { - do - r = uv__dup3(oldfd, newfd, UV__O_CLOEXEC); - while (r == -1 && errno == EBUSY); - if (r != -1) - return r; - if (errno != ENOSYS) - return UV__ERR(errno); - /* Fall through. */ - no_dup3 = 1; - } -#endif - { - int err; - do - r = dup2(oldfd, newfd); -#if defined(__linux__) - while (r == -1 && errno == EBUSY); #else - while (0); /* Never retry. */ -#endif - - if (r == -1) - return UV__ERR(errno); + int err; + int r; - err = uv__cloexec(newfd, 1); - if (err) { - uv__close(newfd); - return err; - } + r = dup2(oldfd, newfd); /* Never retry. */ + if (r == -1) + return UV__ERR(errno); - return r; + err = uv__cloexec(newfd, 1); + if (err != 0) { + uv__close(newfd); + return err; } + + return r; +#endif } @@ -1265,6 +1250,62 @@ int uv_translate_sys_error(int sys_errno) { } +int uv_os_environ(uv_env_item_t** envitems, int* count) { + int i, j, cnt; + uv_env_item_t* envitem; + + *envitems = NULL; + *count = 0; + + for (i = 0; environ[i] != NULL; i++); + + *envitems = uv__calloc(i, sizeof(**envitems)); + + if (*envitems == NULL) + return UV_ENOMEM; + + for (j = 0, cnt = 0; j < i; j++) { + char* buf; + char* ptr; + + if (environ[j] == NULL) + break; + + buf = uv__strdup(environ[j]); + if (buf == NULL) + goto fail; + + ptr = strchr(buf, '='); + if (ptr == NULL) { + uv__free(buf); + continue; + } + + *ptr = '\0'; + + envitem = &(*envitems)[cnt]; + envitem->name = buf; + envitem->value = ptr + 1; + + cnt++; + } + + *count = cnt; + return 0; + +fail: + for (i = 0; i < cnt; i++) { + envitem = &(*envitems)[cnt]; + uv__free(envitem->name); + } + uv__free(*envitems); + + *envitems = NULL; + *count = 0; + return UV_ENOMEM; +} + + int uv_os_getenv(const char* name, char* buffer, size_t* size) { char* var; size_t len; @@ -1488,3 +1529,17 @@ int uv_gettimeofday(uv_timeval64_t* tv) { tv->tv_usec = (int32_t) time.tv_usec; return 0; } + +void uv_sleep(unsigned int msec) { + struct timespec timeout; + int rc; + + timeout.tv_sec = msec / 1000; + timeout.tv_nsec = (msec % 1000) * 1000 * 1000; + + do + rc = nanosleep(&timeout, &timeout); + while (rc == -1 && errno == EINTR); + + assert(rc == 0); +} diff --git a/Utilities/cmlibuv/src/unix/cygwin.c b/Utilities/cmlibuv/src/unix/cygwin.c index 9da20e203aa..169958d55f2 100644 --- a/Utilities/cmlibuv/src/unix/cygwin.c +++ b/Utilities/cmlibuv/src/unix/cygwin.c @@ -48,7 +48,6 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return UV_ENOSYS; } -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - (void)cpu_infos; - (void)count; +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ } diff --git a/Utilities/cmlibuv/src/unix/darwin-proctitle.c b/Utilities/cmlibuv/src/unix/darwin-proctitle.c index e505bdd23f8..5288083ef04 100644 --- a/Utilities/cmlibuv/src/unix/darwin-proctitle.c +++ b/Utilities/cmlibuv/src/unix/darwin-proctitle.c @@ -23,66 +23,61 @@ #include #include +#include #include #include #include #if !TARGET_OS_IPHONE -# include -# include +#include "darwin-stub.h" #endif -#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8) +static int uv__pthread_setname_np(const char* name) { + char namebuf[64]; /* MAXTHREADNAMESIZE */ + int err; -static int (*dynamic_pthread_setname_np)(const char* name); -#if !TARGET_OS_IPHONE -static CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, - const char*, - CFStringEncoding); -static CFBundleRef (*pCFBundleGetBundleWithIdentifier)(CFStringRef); -static void *(*pCFBundleGetDataPointerForName)(CFBundleRef, CFStringRef); -static void *(*pCFBundleGetFunctionPointerForName)(CFBundleRef, CFStringRef); -static CFTypeRef (*pLSGetCurrentApplicationASN)(void); -static OSStatus (*pLSSetApplicationInformationItem)(int, - CFTypeRef, - CFStringRef, - CFStringRef, - CFDictionaryRef*); -static void* application_services_handle; -static void* core_foundation_handle; -static CFBundleRef launch_services_bundle; -static CFStringRef* display_name_key; -static CFDictionaryRef (*pCFBundleGetInfoDictionary)(CFBundleRef); -static CFBundleRef (*pCFBundleGetMainBundle)(void); -static CFBundleRef hi_services_bundle; -static OSStatus (*pSetApplicationIsDaemon)(int); -static CFDictionaryRef (*pLSApplicationCheckIn)(int, CFDictionaryRef); -static void (*pLSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t, - void*); - - -UV_DESTRUCTOR(static void uv__set_process_title_platform_fini(void)) { - if (core_foundation_handle != NULL) { - dlclose(core_foundation_handle); - core_foundation_handle = NULL; - } + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; - if (application_services_handle != NULL) { - dlclose(application_services_handle); - application_services_handle = NULL; - } -} -#endif /* !TARGET_OS_IPHONE */ + err = pthread_setname_np(namebuf); + if (err) + return UV__ERR(err); + return 0; +} -void uv__set_process_title_platform_init(void) { - /* pthread_setname_np() first appeared in OS X 10.6 and iOS 3.2. */ - *(void **)(&dynamic_pthread_setname_np) = - dlsym(RTLD_DEFAULT, "pthread_setname_np"); -#if !TARGET_OS_IPHONE +int uv__set_process_title(const char* title) { +#if TARGET_OS_IPHONE + return uv__pthread_setname_np(title); +#else + CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, + const char*, + CFStringEncoding); + CFBundleRef (*pCFBundleGetBundleWithIdentifier)(CFStringRef); + void *(*pCFBundleGetDataPointerForName)(CFBundleRef, CFStringRef); + void *(*pCFBundleGetFunctionPointerForName)(CFBundleRef, CFStringRef); + CFTypeRef (*pLSGetCurrentApplicationASN)(void); + OSStatus (*pLSSetApplicationInformationItem)(int, + CFTypeRef, + CFStringRef, + CFStringRef, + CFDictionaryRef*); + void* application_services_handle; + void* core_foundation_handle; + CFBundleRef launch_services_bundle; + CFStringRef* display_name_key; + CFDictionaryRef (*pCFBundleGetInfoDictionary)(CFBundleRef); + CFBundleRef (*pCFBundleGetMainBundle)(void); + CFDictionaryRef (*pLSApplicationCheckIn)(int, CFDictionaryRef); + void (*pLSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t, + void*); + CFTypeRef asn; + int err; + + err = UV_ENOENT; application_services_handle = dlopen("/System/Library/Frameworks/" "ApplicationServices.framework/" "Versions/A/ApplicationServices", @@ -111,6 +106,8 @@ void uv__set_process_title_platform_init(void) { goto out; } +#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8) + launch_services_bundle = pCFBundleGetBundleWithIdentifier(S("com.apple.LaunchServices")); @@ -141,58 +138,55 @@ void uv__set_process_title_platform_init(void) { "CFBundleGetInfoDictionary"); *(void **)(&pCFBundleGetMainBundle) = dlsym(core_foundation_handle, "CFBundleGetMainBundle"); - if (pCFBundleGetInfoDictionary == NULL || pCFBundleGetMainBundle == NULL) goto out; - /* Black 10.9 magic, to remove (Not responding) mark in Activity Monitor */ - hi_services_bundle = - pCFBundleGetBundleWithIdentifier(S("com.apple.HIServices")); - - if (hi_services_bundle == NULL) - goto out; - - *(void **)(&pSetApplicationIsDaemon) = pCFBundleGetFunctionPointerForName( - hi_services_bundle, - S("SetApplicationIsDaemon")); *(void **)(&pLSApplicationCheckIn) = pCFBundleGetFunctionPointerForName( launch_services_bundle, S("_LSApplicationCheckIn")); + + if (pLSApplicationCheckIn == NULL) + goto out; + *(void **)(&pLSSetApplicationLaunchServicesServerConnectionStatus) = pCFBundleGetFunctionPointerForName( launch_services_bundle, S("_LSSetApplicationLaunchServicesServerConnectionStatus")); - if (pSetApplicationIsDaemon == NULL || - pLSApplicationCheckIn == NULL || - pLSSetApplicationLaunchServicesServerConnectionStatus == NULL) { + if (pLSSetApplicationLaunchServicesServerConnectionStatus == NULL) + goto out; + + pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL); + + /* Check into process manager?! */ + pLSApplicationCheckIn(-2, + pCFBundleGetInfoDictionary(pCFBundleGetMainBundle())); + + asn = pLSGetCurrentApplicationASN(); + + err = UV_EBUSY; + if (asn == NULL) + goto out; + + err = UV_EINVAL; + if (pLSSetApplicationInformationItem(-2, /* Magic value. */ + asn, + *display_name_key, + S(title), + NULL) != noErr) { goto out; } - return; + uv__pthread_setname_np(title); /* Don't care if it fails. */ + err = 0; out: - uv__set_process_title_platform_fini(); -#endif /* !TARGET_OS_IPHONE */ -} + if (core_foundation_handle != NULL) + dlclose(core_foundation_handle); + if (application_services_handle != NULL) + dlclose(application_services_handle); -void uv__set_process_title(const char* title) { -#if !TARGET_OS_IPHONE - if (core_foundation_handle != NULL && pSetApplicationIsDaemon(1) != noErr) { - CFTypeRef asn; - pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL); - pLSApplicationCheckIn(/* Magic value */ -2, - pCFBundleGetInfoDictionary(pCFBundleGetMainBundle())); - asn = pLSGetCurrentApplicationASN(); - pLSSetApplicationInformationItem(/* Magic value */ -2, asn, - *display_name_key, S(title), NULL); - } + return err; #endif /* !TARGET_OS_IPHONE */ - - if (dynamic_pthread_setname_np != NULL) { - char namebuf[64]; /* MAXTHREADNAMESIZE */ - uv__strscpy(namebuf, title, sizeof(namebuf)); - dynamic_pthread_setname_np(namebuf); - } } diff --git a/Utilities/cmlibuv/src/unix/darwin-stub.h b/Utilities/cmlibuv/src/unix/darwin-stub.h new file mode 100644 index 00000000000..b93cf67c596 --- /dev/null +++ b/Utilities/cmlibuv/src/unix/darwin-stub.h @@ -0,0 +1,97 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_DARWIN_STUB_H_ +#define UV_DARWIN_STUB_H_ + +#include + +struct CFArrayCallBacks; +struct CFRunLoopSourceContext; +struct FSEventStreamContext; + +typedef double CFAbsoluteTime; +typedef double CFTimeInterval; +typedef int FSEventStreamEventFlags; +typedef int OSStatus; +typedef long CFIndex; +typedef struct CFArrayCallBacks CFArrayCallBacks; +typedef struct CFRunLoopSourceContext CFRunLoopSourceContext; +typedef struct FSEventStreamContext FSEventStreamContext; +typedef uint32_t FSEventStreamCreateFlags; +typedef uint64_t FSEventStreamEventId; +typedef unsigned CFStringEncoding; +typedef void* CFAllocatorRef; +typedef void* CFArrayRef; +typedef void* CFBundleRef; +typedef void* CFDictionaryRef; +typedef void* CFRunLoopRef; +typedef void* CFRunLoopSourceRef; +typedef void* CFStringRef; +typedef void* CFTypeRef; +typedef void* FSEventStreamRef; + +typedef void (*FSEventStreamCallback)(const FSEventStreamRef, + void*, + size_t, + void*, + const FSEventStreamEventFlags*, + const FSEventStreamEventId*); + +struct CFRunLoopSourceContext { + CFIndex version; + void* info; + void* pad[7]; + void (*perform)(void*); +}; + +struct FSEventStreamContext { + CFIndex version; + void* info; + void* pad[3]; +}; + +static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100; +static const OSStatus noErr = 0; + +static const FSEventStreamEventId kFSEventStreamEventIdSinceNow = -1; + +static const int kFSEventStreamCreateFlagNoDefer = 2; +static const int kFSEventStreamCreateFlagFileEvents = 16; + +static const int kFSEventStreamEventFlagEventIdsWrapped = 8; +static const int kFSEventStreamEventFlagHistoryDone = 16; +static const int kFSEventStreamEventFlagItemChangeOwner = 0x4000; +static const int kFSEventStreamEventFlagItemCreated = 0x100; +static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x2000; +static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x400; +static const int kFSEventStreamEventFlagItemIsDir = 0x20000; +static const int kFSEventStreamEventFlagItemModified = 0x1000; +static const int kFSEventStreamEventFlagItemRemoved = 0x200; +static const int kFSEventStreamEventFlagItemRenamed = 0x800; +static const int kFSEventStreamEventFlagItemXattrMod = 0x8000; +static const int kFSEventStreamEventFlagKernelDropped = 4; +static const int kFSEventStreamEventFlagMount = 64; +static const int kFSEventStreamEventFlagRootChanged = 32; +static const int kFSEventStreamEventFlagUnmount = 128; +static const int kFSEventStreamEventFlagUserDropped = 2; + +#endif /* UV_DARWIN_STUB_H_ */ diff --git a/Utilities/cmlibuv/src/unix/darwin.c b/Utilities/cmlibuv/src/unix/darwin.c index e4cd8ff7e0c..654aba26b1f 100644 --- a/Utilities/cmlibuv/src/unix/darwin.c +++ b/Utilities/cmlibuv/src/unix/darwin.c @@ -110,7 +110,7 @@ uint64_t uv_get_total_memory(void) { int which[] = {CTL_HW, HW_MEMSIZE}; size_t size = sizeof(info); - if (sysctl(which, 2, &info, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) return UV__ERR(errno); return (uint64_t) info; @@ -127,7 +127,7 @@ void uv_loadavg(double avg[3]) { size_t size = sizeof(info); int which[] = {CTL_VM, VM_LOADAVG}; - if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return; + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return; avg[0] = (double) info.ldavg[0] / info.fscale; avg[1] = (double) info.ldavg[1] / info.fscale; @@ -162,7 +162,7 @@ int uv_uptime(double* uptime) { size_t size = sizeof(info); static int which[] = {CTL_KERN, KERN_BOOTTIME}; - if (sysctl(which, 2, &info, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) return UV__ERR(errno); now = time(NULL); @@ -223,14 +223,3 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return 0; } - - -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; - - for (i = 0; i < count; i++) { - uv__free(cpu_infos[i].model); - } - - uv__free(cpu_infos); -} diff --git a/Utilities/cmlibuv/src/unix/freebsd.c b/Utilities/cmlibuv/src/unix/freebsd.c index 7de88d6a52f..ef77e127c26 100644 --- a/Utilities/cmlibuv/src/unix/freebsd.c +++ b/Utilities/cmlibuv/src/unix/freebsd.c @@ -95,7 +95,7 @@ int uv_exepath(char* buffer, size_t* size) { mib[3] = -1; abspath_size = sizeof abspath; - if (sysctl(mib, 4, abspath, &abspath_size, NULL, 0)) + if (sysctl(mib, ARRAY_SIZE(mib), abspath, &abspath_size, NULL, 0)) return UV__ERR(errno); assert(abspath_size > 0); @@ -130,7 +130,7 @@ uint64_t uv_get_total_memory(void) { size_t size = sizeof(info); - if (sysctl(which, 2, &info, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) return UV__ERR(errno); return (uint64_t) info; @@ -147,7 +147,7 @@ void uv_loadavg(double avg[3]) { size_t size = sizeof(info); int which[] = {CTL_VM, VM_LOADAVG}; - if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return; + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return; avg[0] = (double) info.ldavg[0] / info.fscale; avg[1] = (double) info.ldavg[1] / info.fscale; @@ -168,7 +168,7 @@ int uv_resident_set_memory(size_t* rss) { kinfo_size = sizeof(kinfo); - if (sysctl(mib, 4, &kinfo, &kinfo_size, NULL, 0)) + if (sysctl(mib, ARRAY_SIZE(mib), &kinfo, &kinfo_size, NULL, 0)) return UV__ERR(errno); page_size = getpagesize(); @@ -290,12 +290,26 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; +int uv__sendmmsg(int fd, + struct uv__mmsghdr* mmsg, + unsigned int vlen, + unsigned int flags) { +#if __FreeBSD__ >= 11 + return sendmmsg(fd, mmsg, vlen, flags); +#else + return errno = ENOSYS, -1; +#endif +} - for (i = 0; i < count; i++) { - uv__free(cpu_infos[i].model); - } - uv__free(cpu_infos); +int uv__recvmmsg(int fd, + struct uv__mmsghdr* mmsg, + unsigned int vlen, + unsigned int flags, + struct timespec* timeout) { +#if __FreeBSD__ >= 11 + return recvmmsg(fd, mmsg, vlen, flags, timeout); +#else + return errno = ENOSYS, -1; +#endif } diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c index 5fb34f1be89..f37749c8f03 100644 --- a/Utilities/cmlibuv/src/unix/fs.c +++ b/Utilities/cmlibuv/src/unix/fs.c @@ -30,6 +30,7 @@ #include "internal.h" #include +#include #include #include #include @@ -70,6 +71,20 @@ # include #endif +#if defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) +# include +# include +#elif defined(__sun) || defined(__MVS__) || defined(__NetBSD__) || defined(__HAIKU__) +# include +#else +# include +#endif + #if defined(_AIX) && _XOPEN_SOURCE <= 600 extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */ #endif @@ -190,6 +205,20 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) { } +UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) { + struct timespec ts; + ts.tv_sec = time; + ts.tv_nsec = (uint64_t)(time * 1000000) % 1000000 * 1000; + return ts; +} + +UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) { + struct timeval tv; + tv.tv_sec = time; + tv.tv_usec = (uint64_t)(time * 1000000) % 1000000; + return tv; +} + static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) \ || defined(_AIX71) \ @@ -198,11 +227,13 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { * for the sake of consistency with other platforms. */ struct timespec ts[2]; - ts[0].tv_sec = req->atime; - ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000; - ts[1].tv_sec = req->mtime; - ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000; + ts[0] = uv__fs_to_timespec(req->atime); + ts[1] = uv__fs_to_timespec(req->mtime); +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 + return utimensat(req->file, NULL, ts, 0); +#else return futimens(req->file, ts); +#endif #elif defined(__APPLE__) \ || defined(__DragonFly__) \ || defined(__FreeBSD__) \ @@ -211,10 +242,8 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { || defined(__OpenBSD__) \ || defined(__sun) struct timeval tv[2]; - tv[0].tv_sec = req->atime; - tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000; - tv[1].tv_sec = req->mtime; - tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000; + tv[0] = uv__fs_to_timeval(req->atime); + tv[1] = uv__fs_to_timeval(req->mtime); # if defined(__sun) return futimesat(req->file, NULL, tv); # else @@ -250,22 +279,98 @@ static ssize_t uv__fs_mkdtemp(uv_fs_t* req) { } -static ssize_t uv__fs_open(uv_fs_t* req) { - static int no_cloexec_support; +static int (*uv__mkostemp)(char*, int); + + +static void uv__mkostemp_initonce(void) { + /* z/os doesn't have RTLD_DEFAULT but that's okay + * because it doesn't have mkostemp(O_CLOEXEC) either. + */ +#ifdef RTLD_DEFAULT + uv__mkostemp = (int (*)(char*, int)) dlsym(RTLD_DEFAULT, "mkostemp"); + + /* We don't care about errors, but we do want to clean them up. + * If there has been no error, then dlerror() will just return + * NULL. + */ + dlerror(); +#endif /* RTLD_DEFAULT */ +} + + +static int uv__fs_mkstemp(uv_fs_t* req) { + static uv_once_t once = UV_ONCE_INIT; int r; +#ifdef O_CLOEXEC + static int no_cloexec_support; +#endif + static const char pattern[] = "XXXXXX"; + static const size_t pattern_size = sizeof(pattern) - 1; + char* path; + size_t path_length; + + path = (char*) req->path; + path_length = strlen(path); + + /* EINVAL can be returned for 2 reasons: + 1. The template's last 6 characters were not XXXXXX + 2. open() didn't support O_CLOEXEC + We want to avoid going to the fallback path in case + of 1, so it's manually checked before. */ + if (path_length < pattern_size || + strcmp(path + path_length - pattern_size, pattern)) { + errno = EINVAL; + return -1; + } + + uv_once(&once, uv__mkostemp_initonce); - /* Try O_CLOEXEC before entering locks */ - if (no_cloexec_support == 0) { #ifdef O_CLOEXEC - r = open(req->path, req->flags | O_CLOEXEC, req->mode); + if (no_cloexec_support == 0 && uv__mkostemp != NULL) { + r = uv__mkostemp(path, O_CLOEXEC); + if (r >= 0) return r; + + /* If mkostemp() returns EINVAL, it means the kernel doesn't + support O_CLOEXEC, so we just fallback to mkstemp() below. */ if (errno != EINVAL) return r; + + /* We set the static variable so that next calls don't even + try to use mkostemp. */ no_cloexec_support = 1; + } #endif /* O_CLOEXEC */ + + if (req->cb != NULL) + uv_rwlock_rdlock(&req->loop->cloexec_lock); + + r = mkstemp(path); + + /* In case of failure `uv__cloexec` will leave error in `errno`, + * so it is enough to just set `r` to `-1`. + */ + if (r >= 0 && uv__cloexec(r, 1) != 0) { + r = uv__close(r); + if (r != 0) + abort(); + r = -1; } + if (req->cb != NULL) + uv_rwlock_rdunlock(&req->loop->cloexec_lock); + + return r; +} + + +static ssize_t uv__fs_open(uv_fs_t* req) { +#ifdef O_CLOEXEC + return open(req->path, req->flags | O_CLOEXEC, req->mode); +#else /* O_CLOEXEC */ + int r; + if (req->cb != NULL) uv_rwlock_rdlock(&req->loop->cloexec_lock); @@ -285,7 +390,58 @@ static ssize_t uv__fs_open(uv_fs_t* req) { uv_rwlock_rdunlock(&req->loop->cloexec_lock); return r; +#endif /* O_CLOEXEC */ +} + + +#if !HAVE_PREADV +static ssize_t uv__fs_preadv(uv_file fd, + uv_buf_t* bufs, + unsigned int nbufs, + off_t off) { + uv_buf_t* buf; + uv_buf_t* end; + ssize_t result; + ssize_t rc; + size_t pos; + + assert(nbufs > 0); + + result = 0; + pos = 0; + buf = bufs + 0; + end = bufs + nbufs; + + for (;;) { + do + rc = pread(fd, buf->base + pos, buf->len - pos, off + result); + while (rc == -1 && errno == EINTR); + + if (rc == 0) + break; + + if (rc == -1 && result == 0) + return UV__ERR(errno); + + if (rc == -1) + break; /* We read some data so return that, ignore the error. */ + + pos += rc; + result += rc; + + if (pos < buf->len) + continue; + + pos = 0; + buf += 1; + + if (buf == end) + break; + } + + return result; } +#endif static ssize_t uv__fs_read(uv_fs_t* req) { @@ -317,7 +473,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) { if (no_preadv) retry: # endif { - result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off); + result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off); } # if defined(__linux__) else { @@ -481,14 +637,39 @@ static int uv__fs_closedir(uv_fs_t* req) { return 0; } -#if defined(_POSIX_PATH_MAX) -# define UV__FS_PATH_MAX _POSIX_PATH_MAX -#elif defined(PATH_MAX) -# define UV__FS_PATH_MAX PATH_MAX +static int uv__fs_statfs(uv_fs_t* req) { + uv_statfs_t* stat_fs; +#if defined(__sun) || defined(__MVS__) || defined(__NetBSD__) || defined(__HAIKU__) + struct statvfs buf; + + if (0 != statvfs(req->path, &buf)) #else -# define UV__FS_PATH_MAX_FALLBACK 8192 -# define UV__FS_PATH_MAX UV__FS_PATH_MAX_FALLBACK + struct statfs buf; + + if (0 != statfs(req->path, &buf)) +#endif /* defined(__sun) */ + return -1; + + stat_fs = uv__malloc(sizeof(*stat_fs)); + if (stat_fs == NULL) { + errno = ENOMEM; + return -1; + } + +#if defined(__sun) || defined(__MVS__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) + stat_fs->f_type = 0; /* f_type is not supported. */ +#else + stat_fs->f_type = buf.f_type; #endif + stat_fs->f_bsize = buf.f_bsize; + stat_fs->f_blocks = buf.f_blocks; + stat_fs->f_bfree = buf.f_bfree; + stat_fs->f_bavail = buf.f_bavail; + stat_fs->f_files = buf.f_files; + stat_fs->f_ffree = buf.f_ffree; + req->ptr = stat_fs; + return 0; +} static ssize_t uv__fs_pathmax_size(const char* path) { ssize_t pathmax; @@ -496,7 +677,7 @@ static ssize_t uv__fs_pathmax_size(const char* path) { pathmax = pathconf(path, _PC_PATH_MAX); if (pathmax == -1) - pathmax = UV__FS_PATH_MAX; + pathmax = UV__PATH_MAX; return pathmax; } @@ -505,9 +686,10 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) { ssize_t maxlen; ssize_t len; char* buf; - char* newbuf; -#if defined(UV__FS_PATH_MAX_FALLBACK) +#if defined(_POSIX_PATH_MAX) || defined(PATH_MAX) + maxlen = uv__fs_pathmax_size(req->path); +#else /* We may not have a real PATH_MAX. Read size of link. */ struct stat st; int ret; @@ -525,8 +707,6 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) { for some symlinks, such as those in /proc or /sys. */ if (maxlen == 0) maxlen = uv__fs_pathmax_size(req->path); -#else - maxlen = uv__fs_pathmax_size(req->path); #endif buf = uv__malloc(maxlen); @@ -549,14 +729,10 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) { /* Uncommon case: resize to make room for the trailing nul byte. */ if (len == maxlen) { - newbuf = uv__realloc(buf, len + 1); + buf = uv__reallocf(buf, len + 1); - if (newbuf == NULL) { - uv__free(buf); + if (buf == NULL) return -1; - } - - buf = newbuf; } buf[len] = '\0'; @@ -816,10 +992,8 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { * for the sake of consistency with other platforms. */ struct timespec ts[2]; - ts[0].tv_sec = req->atime; - ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000; - ts[1].tv_sec = req->mtime; - ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000; + ts[0] = uv__fs_to_timespec(req->atime); + ts[1] = uv__fs_to_timespec(req->mtime); return utimensat(AT_FDCWD, req->path, ts, 0); #elif defined(__APPLE__) \ || defined(__DragonFly__) \ @@ -828,10 +1002,8 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { || defined(__NetBSD__) \ || defined(__OpenBSD__) struct timeval tv[2]; - tv[0].tv_sec = req->atime; - tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000; - tv[1].tv_sec = req->mtime; - tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000; + tv[0] = uv__fs_to_timeval(req->atime); + tv[1] = uv__fs_to_timeval(req->mtime); return utimes(req->path, tv); #elif defined(_AIX) \ && !defined(_AIX71) @@ -854,6 +1026,31 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { } +static ssize_t uv__fs_lutime(uv_fs_t* req) { +#if defined(__linux__) || \ + defined(_AIX71) || \ + defined(__sun) || \ + defined(__HAIKU__) + struct timespec ts[2]; + ts[0] = uv__fs_to_timespec(req->atime); + ts[1] = uv__fs_to_timespec(req->mtime); + return utimensat(AT_FDCWD, req->path, ts, AT_SYMLINK_NOFOLLOW); +#elif defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) + struct timeval tv[2]; + tv[0] = uv__fs_to_timeval(req->atime); + tv[1] = uv__fs_to_timeval(req->mtime); + return lutimes(req->path, tv); +#else + errno = ENOSYS; + return -1; +#endif +} + + static ssize_t uv__fs_write(uv_fs_t* req) { #if defined(__linux__) static int no_pwritev; @@ -918,12 +1115,15 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { uv_fs_t fs_req; uv_file srcfd; uv_file dstfd; - struct stat statsbuf; + struct stat src_statsbuf; + struct stat dst_statsbuf; int dst_flags; int result; int err; - size_t bytes_to_send; - int64_t in_offset; + off_t bytes_to_send; + off_t in_offset; + off_t bytes_written; + size_t bytes_chunk; dstfd = -1; err = 0; @@ -936,7 +1136,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { return srcfd; /* Get the source file's mode. */ - if (fstat(srcfd, &statsbuf)) { + if (fstat(srcfd, &src_statsbuf)) { err = UV__ERR(errno); goto out; } @@ -951,7 +1151,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { &fs_req, req->new_path, dst_flags, - statsbuf.st_mode, + src_statsbuf.st_mode, NULL); uv_fs_req_cleanup(&fs_req); @@ -960,26 +1160,55 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { goto out; } - if (fchmod(dstfd, statsbuf.st_mode) == -1) { + /* Get the destination file's mode. */ + if (fstat(dstfd, &dst_statsbuf)) { err = UV__ERR(errno); goto out; } + /* Check if srcfd and dstfd refer to the same file */ + if (src_statsbuf.st_dev == dst_statsbuf.st_dev && + src_statsbuf.st_ino == dst_statsbuf.st_ino) { + goto out; + } + + if (fchmod(dstfd, src_statsbuf.st_mode) == -1) { + err = UV__ERR(errno); +#ifdef __linux__ + if (err != UV_EPERM) + goto out; + + { + struct statfs s; + + /* fchmod() on CIFS shares always fails with EPERM unless the share is + * mounted with "noperm". As fchmod() is a meaningless operation on such + * shares anyway, detect that condition and squelch the error. + */ + if (fstatfs(dstfd, &s) == -1) + goto out; + + if (s.f_type != /* CIFS */ 0xFF534D42u) + goto out; + } + + err = 0; +#else /* !__linux__ */ + goto out; +#endif /* !__linux__ */ + } + #ifdef FICLONE if (req->flags & UV_FS_COPYFILE_FICLONE || req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { - if (ioctl(dstfd, FICLONE, srcfd) == -1) { - /* If an error occurred that the sendfile fallback also won't handle, or - this is a force clone then exit. Otherwise, fall through to try using - sendfile(). */ - if (errno != ENOTTY && errno != EOPNOTSUPP && errno != EXDEV) { - err = UV__ERR(errno); - goto out; - } else if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { - err = UV_ENOTSUP; - goto out; - } - } else { + if (ioctl(dstfd, FICLONE, srcfd) == 0) { + /* ioctl() with FICLONE succeeded. */ + goto out; + } + /* If an error occurred and force was set, return the error to the caller; + * fall back to sendfile() when force was not set. */ + if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { + err = UV__ERR(errno); goto out; } } @@ -990,21 +1219,23 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { } #endif - bytes_to_send = statsbuf.st_size; + bytes_to_send = src_statsbuf.st_size; in_offset = 0; while (bytes_to_send != 0) { - err = uv_fs_sendfile(NULL, - &fs_req, - dstfd, - srcfd, - in_offset, - bytes_to_send, - NULL); + bytes_chunk = SSIZE_MAX; + if (bytes_to_send < (off_t) bytes_chunk) + bytes_chunk = bytes_to_send; + uv_fs_sendfile(NULL, &fs_req, dstfd, srcfd, in_offset, bytes_chunk, NULL); + bytes_written = fs_req.result; uv_fs_req_cleanup(&fs_req); - if (err < 0) + + if (bytes_written < 0) { + err = bytes_written; break; - bytes_to_send -= fs_req.result; - in_offset += fs_req.result; + } + + bytes_to_send -= bytes_written; + in_offset += bytes_written; } out: @@ -1151,13 +1382,22 @@ static int uv__fs_statx(int fd, rc = uv__statx(dirfd, path, flags, mode, &statxbuf); - if (rc == -1) { + switch (rc) { + case 0: + break; + case -1: /* EPERM happens when a seccomp filter rejects the system call. * Has been observed with libseccomp < 2.3.3 and docker < 18.04. */ if (errno != EINVAL && errno != EPERM && errno != ENOSYS) return -1; - + /* Fall through. */ + default: + /* Normally on success, zero is returned and On error, -1 is returned. + * Observed on S390 RHEL running in a docker container with statx not + * implemented, rc might return 1 with 0 set as the error code in which + * case we return ENOSYS. + */ no_statx = 1; return UV_ENOSYS; } @@ -1328,10 +1568,12 @@ static void uv__fs_work(struct uv__work* w) { X(FSYNC, uv__fs_fsync(req)); X(FTRUNCATE, ftruncate(req->file, req->off)); X(FUTIME, uv__fs_futime(req)); + X(LUTIME, uv__fs_lutime(req)); X(LSTAT, uv__fs_lstat(req->path, &req->statbuf)); X(LINK, link(req->path, req->new_path)); X(MKDIR, mkdir(req->path, req->mode)); X(MKDTEMP, uv__fs_mkdtemp(req)); + X(MKSTEMP, uv__fs_mkstemp(req)); X(OPEN, uv__fs_open(req)); X(READ, uv__fs_read(req)); X(SCANDIR, uv__fs_scandir(req)); @@ -1344,6 +1586,7 @@ static void uv__fs_work(struct uv__work* w) { X(RMDIR, rmdir(req->path)); X(SENDFILE, uv__fs_sendfile(req)); X(STAT, uv__fs_stat(req->path, &req->statbuf)); + X(STATFS, uv__fs_statfs(req)); X(SYMLINK, symlink(req->path, req->new_path)); X(UNLINK, unlink(req->path)); X(UTIME, uv__fs_utime(req)); @@ -1512,6 +1755,19 @@ int uv_fs_futime(uv_loop_t* loop, POST; } +int uv_fs_lutime(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + double atime, + double mtime, + uv_fs_cb cb) { + INIT(LUTIME); + PATH; + req->atime = atime; + req->mtime = mtime; + POST; +} + int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(LSTAT); @@ -1555,6 +1811,18 @@ int uv_fs_mkdtemp(uv_loop_t* loop, } +int uv_fs_mkstemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb) { + INIT(MKSTEMP); + req->path = uv__strdup(tpl); + if (req->path == NULL) + return UV_ENOMEM; + POST; +} + + int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1773,10 +2041,12 @@ void uv_fs_req_cleanup(uv_fs_t* req) { /* Only necessary for asychronous requests, i.e., requests with a callback. * Synchronous ones don't copy their arguments and have req->path and - * req->new_path pointing to user-owned memory. UV_FS_MKDTEMP is the - * exception to the rule, it always allocates memory. + * req->new_path pointing to user-owned memory. UV_FS_MKDTEMP and + * UV_FS_MKSTEMP are the exception to the rule, they always allocate memory. */ - if (req->path != NULL && (req->cb != NULL || req->fs_type == UV_FS_MKDTEMP)) + if (req->path != NULL && + (req->cb != NULL || + req->fs_type == UV_FS_MKDTEMP || req->fs_type == UV_FS_MKSTEMP)) uv__free((void*) req->path); /* Memory is shared with req->new_path. */ req->path = NULL; @@ -1816,3 +2086,17 @@ int uv_fs_copyfile(uv_loop_t* loop, req->flags = flags; POST; } + + +int uv_fs_statfs(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + INIT(STATFS); + PATH; + POST; +} + +int uv_fs_get_system_error(const uv_fs_t* req) { + return -req->result; +} diff --git a/Utilities/cmlibuv/src/unix/fsevents.c b/Utilities/cmlibuv/src/unix/fsevents.c index ddacda31fef..a51f29b3f6d 100644 --- a/Utilities/cmlibuv/src/unix/fsevents.c +++ b/Utilities/cmlibuv/src/unix/fsevents.c @@ -41,34 +41,33 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) { #else /* TARGET_OS_IPHONE */ +#include "darwin-stub.h" + #include #include #include #include -#include -#include - -/* These are macros to avoid "initializer element is not constant" errors - * with old versions of gcc. - */ -#define kFSEventsModified (kFSEventStreamEventFlagItemFinderInfoMod | \ - kFSEventStreamEventFlagItemModified | \ - kFSEventStreamEventFlagItemInodeMetaMod | \ - kFSEventStreamEventFlagItemChangeOwner | \ - kFSEventStreamEventFlagItemXattrMod) - -#define kFSEventsRenamed (kFSEventStreamEventFlagItemCreated | \ - kFSEventStreamEventFlagItemRemoved | \ - kFSEventStreamEventFlagItemRenamed) - -#define kFSEventsSystem (kFSEventStreamEventFlagUserDropped | \ - kFSEventStreamEventFlagKernelDropped | \ - kFSEventStreamEventFlagEventIdsWrapped | \ - kFSEventStreamEventFlagHistoryDone | \ - kFSEventStreamEventFlagMount | \ - kFSEventStreamEventFlagUnmount | \ - kFSEventStreamEventFlagRootChanged) +static const int kFSEventsModified = + kFSEventStreamEventFlagItemChangeOwner | + kFSEventStreamEventFlagItemFinderInfoMod | + kFSEventStreamEventFlagItemInodeMetaMod | + kFSEventStreamEventFlagItemModified | + kFSEventStreamEventFlagItemXattrMod; + +static const int kFSEventsRenamed = + kFSEventStreamEventFlagItemCreated | + kFSEventStreamEventFlagItemRemoved | + kFSEventStreamEventFlagItemRenamed; + +static const int kFSEventsSystem = + kFSEventStreamEventFlagUserDropped | + kFSEventStreamEventFlagKernelDropped | + kFSEventStreamEventFlagEventIdsWrapped | + kFSEventStreamEventFlagHistoryDone | + kFSEventStreamEventFlagMount | + kFSEventStreamEventFlagUnmount | + kFSEventStreamEventFlagRootChanged; typedef struct uv__fsevents_event_s uv__fsevents_event_t; typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t; @@ -148,7 +147,7 @@ static void (*pFSEventStreamRelease)(FSEventStreamRef); static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef, CFRunLoopRef, CFStringRef); -static Boolean (*pFSEventStreamStart)(FSEventStreamRef); +static int (*pFSEventStreamStart)(FSEventStreamRef); static void (*pFSEventStreamStop)(FSEventStreamRef); #define UV__FSEVENTS_PROCESS(handle, block) \ @@ -215,7 +214,7 @@ static void uv__fsevents_push_event(uv_fs_event_t* handle, /* Runs in CF thread, when there're events in FSEventStream */ -static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, +static void uv__fsevents_event_cb(const FSEventStreamRef streamRef, void* info, size_t numEvents, void* eventPaths, @@ -263,10 +262,12 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, if (len < handle->realpath_len) continue; + /* Make sure that realpath actually named a directory, + * (unless watching root, which alone keeps a trailing slash on the realpath) + * or that we matched the whole string */ if (handle->realpath_len != len && + handle->realpath_len > 1 && path[handle->realpath_len] != '/') - /* Make sure that realpath actually named a directory, - * or that we matched the whole string */ continue; if (memcmp(path, handle->realpath, handle->realpath_len) != 0) @@ -338,11 +339,8 @@ static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { FSEventStreamCreateFlags flags; /* Initialize context */ - ctx.version = 0; + memset(&ctx, 0, sizeof(ctx)); ctx.info = loop; - ctx.retain = NULL; - ctx.release = NULL; - ctx.copyDescription = NULL; latency = 0.05; @@ -745,6 +743,8 @@ static void* uv__cf_loop_runner(void* arg) { state->signal_source, *pkCFRunLoopDefaultMode); + state->loop = NULL; + return NULL; } @@ -797,13 +797,14 @@ int uv__cf_loop_signal(uv_loop_t* loop, uv_mutex_lock(&loop->cf_mutex); QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member); - uv_mutex_unlock(&loop->cf_mutex); state = loop->cf_state; assert(state != NULL); pCFRunLoopSourceSignal(state->signal_source); pCFRunLoopWakeUp(state->loop); + uv_mutex_unlock(&loop->cf_mutex); + return 0; } diff --git a/Utilities/cmlibuv/src/unix/haiku.c b/Utilities/cmlibuv/src/unix/haiku.c index 7708851c2a5..cf17d836b4c 100644 --- a/Utilities/cmlibuv/src/unix/haiku.c +++ b/Utilities/cmlibuv/src/unix/haiku.c @@ -165,12 +165,3 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return 0; } - -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; - - for (i = 0; i < count; i++) - uv__free(cpu_infos[i].model); - - uv__free(cpu_infos); -} diff --git a/Utilities/cmlibuv/src/unix/ibmi.c b/Utilities/cmlibuv/src/unix/ibmi.c index c7e105136e1..e4c5122bc9e 100644 --- a/Utilities/cmlibuv/src/unix/ibmi.c +++ b/Utilities/cmlibuv/src/unix/ibmi.c @@ -56,6 +56,7 @@ #include #include +#include typedef struct { @@ -98,24 +99,91 @@ typedef struct { } SSTS0200; +typedef struct { + char header[208]; + unsigned char loca_adapter_address[12]; +} LIND0500; + + +typedef struct { + int bytes_provided; + int bytes_available; + char msgid[7]; +} errcode_s; + + +static const unsigned char e2a[256] = { + 0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, + 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, + 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, + 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33, + 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94, + 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, + 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, + 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208, + 209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, + 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, + 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255}; + + +static const unsigned char a2e[256] = { + 0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31, + 64, 79, 127, 123, 91, 108, 80, 125, 77, 93, 92, 78, 107, 96, 75, 97, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122, 94, 76, 126, 110, 111, + 124, 193, 194, 195, 196, 197, 198, 199, 200, 201, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 226, 227, 228, 229, 230, 231, 232, 233, 74, 224, 90, 95, 109, + 121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 162, 163, 164, 165, 166, 167, 168, 169, 192, 106, 208, 161, 7, + 32, 33, 34, 35, 36, 21, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27, + 48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59, 4, 20, 62, 225, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 98, 99, 100, 101, 102, 103, 104, 105, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 128, 138, 139, 140, 141, 142, 143, 144, 154, 155, 156, 157, 158, + 159, 160, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 202, 203, 204, 205, 206, 207, 218, 219, + 220, 221, 222, 223, 234, 235, 236, 237, 238, 239, 250, 251, 252, 253, 254, 255}; + + +static void iconv_e2a(unsigned char src[], unsigned char dst[], size_t length) { + size_t i; + for (i = 0; i < length; i++) + dst[i] = e2a[src[i]]; +} + + +static void iconv_a2e(const char* src, unsigned char dst[], size_t length) { + size_t srclen; + size_t i; + + srclen = strlen(src); + if (srclen > length) + abort(); + for (i = 0; i < srclen; i++) + dst[i] = a2e[src[i]]; + /* padding the remaining part with spaces */ + for (; i < length; i++) + dst[i] = a2e[' ']; +} + + static int get_ibmi_system_status(SSTS0200* rcvr) { /* rcvrlen is input parameter 2 to QWCRSSTS */ unsigned int rcvrlen = sizeof(*rcvr); + unsigned char format[8], reset_status[10]; - /* format is input parameter 3 to QWCRSSTS ("SSTS0200" in EBCDIC) */ - unsigned char format[] = {0xE2, 0xE2, 0xE3, 0xE2, 0xF0, 0xF2, 0xF0, 0xF0}; - - /* reset_status is input parameter 4 to QWCRSSTS ("*NO " in EBCDIC) */ - unsigned char reset_status[] = { - 0x5C, 0xD5, 0xD6, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 - }; + /* format is input parameter 3 to QWCRSSTS */ + iconv_a2e("SSTS0200", format, sizeof(format)); + /* reset_status is input parameter 4 */ + iconv_a2e("*NO", reset_status, sizeof(reset_status)); /* errcode is input parameter 5 to QWCRSSTS */ - struct _errcode { - int bytes_provided; - int bytes_available; - char msgid[7]; - } errcode; + errcode_s errcode; /* qwcrssts_pointer is the 16-byte tagged system pointer to QWCRSSTS */ ILEpointer __attribute__((aligned(16))) qwcrssts_pointer; @@ -145,7 +213,7 @@ static int get_ibmi_system_status(SSTS0200* rcvr) { qwcrssts_argv[5] = NULL; /* Call the IBM i QWCRSSTS API from PASE */ - rc = _PGMCALL(&qwcrssts_pointer, (void**)&qwcrssts_argv, 0); + rc = _PGMCALL(&qwcrssts_pointer, qwcrssts_argv, 0); return rc; } @@ -157,19 +225,7 @@ uint64_t uv_get_free_memory(void) { if (get_ibmi_system_status(&rcvr)) return 0; - /* The amount of main storage, in kilobytes, in the system. */ - uint64_t main_storage_size = rcvr.main_storage_size; - - /* The current amount of storage in use for temporary objects. - * in millions (M) of bytes. - */ - uint64_t current_unprotected_storage_used = - rcvr.current_unprotected_storage_used * 1024ULL; - - uint64_t free_storage_size = - (main_storage_size - current_unprotected_storage_used) * 1024ULL; - - return free_storage_size < 0 ? 0 : free_storage_size; + return (uint64_t)rcvr.main_storage_size * 1024ULL; } @@ -248,3 +304,159 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return 0; } + +static int get_ibmi_physical_address(const char* line, char (*phys_addr)[6]) { + LIND0500 rcvr; + /* rcvrlen is input parameter 2 to QDCRLIND */ + unsigned int rcvrlen = sizeof(rcvr); + unsigned char format[8], line_name[10]; + unsigned char mac_addr[sizeof(rcvr.loca_adapter_address)]; + int c[6]; + + /* format is input parameter 3 to QDCRLIND */ + iconv_a2e("LIND0500", format, sizeof(format)); + + /* line_name is input parameter 4 to QDCRLIND */ + iconv_a2e(line, line_name, sizeof(line_name)); + + /* err is input parameter 5 to QDCRLIND */ + errcode_s err; + + /* qwcrssts_pointer is the 16-byte tagged system pointer to QDCRLIND */ + ILEpointer __attribute__((aligned(16))) qdcrlind_pointer; + + /* qwcrssts_argv is the array of argument pointers to QDCRLIND */ + void* qdcrlind_argv[6]; + + /* Set the IBM i pointer to the QSYS/QDCRLIND *PGM object */ + int rc = _RSLOBJ2(&qdcrlind_pointer, RSLOBJ_TS_PGM, "QDCRLIND", "QSYS"); + + if (rc != 0) + return rc; + + /* initialize the QDCRLIND returned info structure */ + memset(&rcvr, 0, sizeof(rcvr)); + + /* initialize the QDCRLIND error code structure */ + memset(&err, 0, sizeof(err)); + err.bytes_provided = sizeof(err); + + /* initialize the array of argument pointers for the QDCRLIND API */ + qdcrlind_argv[0] = &rcvr; + qdcrlind_argv[1] = &rcvrlen; + qdcrlind_argv[2] = &format; + qdcrlind_argv[3] = &line_name; + qdcrlind_argv[4] = &err; + qdcrlind_argv[5] = NULL; + + /* Call the IBM i QDCRLIND API from PASE */ + rc = _PGMCALL(&qdcrlind_pointer, qdcrlind_argv, 0); + if (rc != 0) + return rc; + + /* convert ebcdic loca_adapter_address to ascii first */ + iconv_e2a(rcvr.loca_adapter_address, mac_addr, + sizeof(rcvr.loca_adapter_address)); + + /* convert loca_adapter_address(char[12]) to phys_addr(char[6]) */ + int r = sscanf(mac_addr, "%02x%02x%02x%02x%02x%02x", + &c[0], &c[1], &c[2], &c[3], &c[4], &c[5]); + + if (r == ARRAY_SIZE(c)) { + (*phys_addr)[0] = c[0]; + (*phys_addr)[1] = c[1]; + (*phys_addr)[2] = c[2]; + (*phys_addr)[3] = c[3]; + (*phys_addr)[4] = c[4]; + (*phys_addr)[5] = c[5]; + } else { + memset(*phys_addr, 0, sizeof(*phys_addr)); + rc = -1; + } + return rc; +} + + +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + uv_interface_address_t* address; + struct ifaddrs_pase *ifap = NULL, *cur; + int inet6, r = 0; + + *count = 0; + *addresses = NULL; + + if (Qp2getifaddrs(&ifap)) + return UV_ENOSYS; + + /* The first loop to get the size of the array to be allocated */ + for (cur = ifap; cur; cur = cur->ifa_next) { + if (!(cur->ifa_addr->sa_family == AF_INET6 || + cur->ifa_addr->sa_family == AF_INET)) + continue; + + if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING)) + continue; + + (*count)++; + } + + if (*count == 0) { + Qp2freeifaddrs(ifap); + return 0; + } + + /* Alloc the return interface structs */ + *addresses = uv__calloc(*count, sizeof(**addresses)); + if (*addresses == NULL) { + Qp2freeifaddrs(ifap); + return UV_ENOMEM; + } + address = *addresses; + + /* The second loop to fill in the array */ + for (cur = ifap; cur; cur = cur->ifa_next) { + if (!(cur->ifa_addr->sa_family == AF_INET6 || + cur->ifa_addr->sa_family == AF_INET)) + continue; + + if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING)) + continue; + + address->name = uv__strdup(cur->ifa_name); + + inet6 = (cur->ifa_addr->sa_family == AF_INET6); + + if (inet6) { + address->address.address6 = *((struct sockaddr_in6*)cur->ifa_addr); + address->netmask.netmask6 = *((struct sockaddr_in6*)cur->ifa_netmask); + address->netmask.netmask6.sin6_family = AF_INET6; + } else { + address->address.address4 = *((struct sockaddr_in*)cur->ifa_addr); + address->netmask.netmask4 = *((struct sockaddr_in*)cur->ifa_netmask); + address->netmask.netmask4.sin_family = AF_INET; + } + address->is_internal = cur->ifa_flags & IFF_LOOPBACK ? 1 : 0; + if (!address->is_internal) { + int rc = get_ibmi_physical_address(address->name, &address->phys_addr); + if (rc != 0) + r = rc; + } + + address++; + } + + Qp2freeifaddrs(ifap); + return r; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { + int i; + + for (i = 0; i < count; ++i) { + uv__free(addresses[i].name); + } + + uv__free(addresses); +} + diff --git a/Utilities/cmlibuv/src/unix/internal.h b/Utilities/cmlibuv/src/unix/internal.h index b43c0b19069..25496028015 100644 --- a/Utilities/cmlibuv/src/unix/internal.h +++ b/Utilities/cmlibuv/src/unix/internal.h @@ -25,11 +25,13 @@ #include "uv-common.h" #include +#include /* _POSIX_PATH_MAX, PATH_MAX */ #include /* abort */ #include /* strrchr */ -#include /* O_CLOEXEC, may be */ +#include /* O_CLOEXEC and O_NONBLOCK, if supported. */ #include #include +#include #if defined(__STRICT_ANSI__) # define inline __inline @@ -60,6 +62,14 @@ # include #endif +#if defined(_POSIX_PATH_MAX) +# define UV__PATH_MAX _POSIX_PATH_MAX +#elif defined(PATH_MAX) +# define UV__PATH_MAX PATH_MAX +#else +# define UV__PATH_MAX 8192 +#endif + #if defined(CMAKE_BOOTSTRAP) # undef pthread_atfork # define pthread_atfork(prepare, parent, child) \ @@ -106,10 +116,8 @@ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); #if defined(__clang__) || \ defined(__GNUC__) || \ defined(__INTEL_COMPILER) -# define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration # define UV_UNUSED(declaration) __attribute__((unused)) declaration #else -# define UV_DESTRUCTOR(declaration) declaration # define UV_UNUSED(declaration) declaration #endif @@ -271,6 +279,12 @@ uv_handle_type uv__handle_type(int fd); FILE* uv__open_file(const char* path); int uv__getpwuid_r(uv_passwd_t* pwd); +/* random */ +int uv__random_devurandom(void* buf, size_t buflen); +int uv__random_getrandom(void* buf, size_t buflen); +int uv__random_getentropy(void* buf, size_t buflen); +int uv__random_readpath(const char* path, void* buf, size_t buflen); +int uv__random_sysctl(void* buf, size_t buflen); #if defined(__APPLE__) && !defined(CMAKE_BOOTSTRAP) int uv___stream_fd(const uv_stream_t* handle); @@ -279,13 +293,12 @@ int uv___stream_fd(const uv_stream_t* handle); #define uv__stream_fd(handle) ((handle)->io_watcher.fd) #endif /* defined(__APPLE__) */ -#ifdef UV__O_NONBLOCK -# define UV__F_NONBLOCK UV__O_NONBLOCK +#ifdef O_NONBLOCK +# define UV__F_NONBLOCK O_NONBLOCK #else # define UV__F_NONBLOCK 1 #endif -int uv__make_socketpair(int fds[2], int flags); int uv__make_pipe(int fds[2], int flags); #if defined(__APPLE__) @@ -323,4 +336,27 @@ int uv__getsockpeername(const uv_handle_t* handle, struct sockaddr* name, int* namelen); +#if defined(__linux__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) +#define HAVE_MMSG 1 +struct uv__mmsghdr { + struct msghdr msg_hdr; + unsigned int msg_len; +}; + +int uv__recvmmsg(int fd, + struct uv__mmsghdr* mmsg, + unsigned int vlen, + unsigned int flags, + struct timespec* timeout); +int uv__sendmmsg(int fd, + struct uv__mmsghdr* mmsg, + unsigned int vlen, + unsigned int flags); +#else +#define HAVE_MMSG 0 +#endif + + #endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/Utilities/cmlibuv/src/unix/kqueue.c b/Utilities/cmlibuv/src/unix/kqueue.c index c04e7a485cf..ad09f403131 100644 --- a/Utilities/cmlibuv/src/unix/kqueue.c +++ b/Utilities/cmlibuv/src/unix/kqueue.c @@ -454,10 +454,26 @@ int uv_fs_event_start(uv_fs_event_t* handle, const char* path, unsigned int flags) { int fd; +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + struct stat statbuf; +#endif if (uv__is_active(handle)) return UV_EINVAL; + handle->cb = cb; + handle->path = uv__strdup(path); + if (handle->path == NULL) + return UV_ENOMEM; + + /* TODO open asynchronously - but how do we report back errors? */ + fd = open(handle->path, O_RDONLY); + if (fd == -1) { + uv__free(handle->path); + handle->path = NULL; + return UV__ERR(errno); + } + #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 /* Nullify field to perform checks later */ handle->cf_cb = NULL; @@ -465,14 +481,17 @@ int uv_fs_event_start(uv_fs_event_t* handle, handle->realpath_len = 0; handle->cf_flags = flags; + if (fstat(fd, &statbuf)) + goto fallback; + /* FSEvents works only with directories */ + if (!(statbuf.st_mode & S_IFDIR)) + goto fallback; + if (!uv__has_forked_with_cfrunloop) { int r; - /* The fallback fd is not used */ + /* The fallback fd is no longer needed */ + uv__close_nocheckstdio(fd); handle->event_watcher.fd = -1; - handle->path = uv__strdup(path); - if (handle->path == NULL) - return UV_ENOMEM; - handle->cb = cb; r = uv__fsevents_init(handle); if (r == 0) { uv__handle_start(handle); @@ -482,20 +501,9 @@ int uv_fs_event_start(uv_fs_event_t* handle, } return r; } +fallback: #endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ - /* TODO open asynchronously - but how do we report back errors? */ - fd = open(path, O_RDONLY); - if (fd == -1) - return UV__ERR(errno); - - handle->path = uv__strdup(path); - if (handle->path == NULL) { - uv__close_nocheckstdio(fd); - return UV_ENOMEM; - } - - handle->cb = cb; uv__handle_start(handle); uv__io_init(&handle->event_watcher, uv__fs_event, fd); uv__io_start(handle->loop, &handle->event_watcher, POLLIN); @@ -514,7 +522,7 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { uv__handle_stop(handle); #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - if (!uv__has_forked_with_cfrunloop) + if (!uv__has_forked_with_cfrunloop && handle->cf_cb != NULL) r = uv__fsevents_close(handle); #endif diff --git a/Utilities/cmlibuv/src/unix/linux-core.c b/Utilities/cmlibuv/src/unix/linux-core.c index b539beb86ae..99cbb1c8fd7 100644 --- a/Utilities/cmlibuv/src/unix/linux-core.c +++ b/Utilities/cmlibuv/src/unix/linux-core.c @@ -90,7 +90,12 @@ int uv__platform_loop_init(uv_loop_t* loop) { * a.k.a. Lollipop. Since EPOLL_CLOEXEC is an alias for O_CLOEXEC on all * architectures, we just use that instead. */ +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 + fd = -1; + errno = ENOSYS; +#else fd = epoll_create1(O_CLOEXEC); +#endif /* epoll_create1() can fail either because it's not implemented (old kernel) * or because it doesn't understand the O_CLOEXEC flag. @@ -203,6 +208,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { * that being the largest value I have seen in the wild (and only once.) */ static const int max_safe_timeout = 1789569; + static int no_epoll_pwait; + static int no_epoll_wait; struct epoll_event events[1024]; struct epoll_event* pe; struct epoll_event e; @@ -210,7 +217,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { QUEUE* q; uv__io_t* w; sigset_t sigset; - sigset_t* psigset; + uint64_t sigmask; uint64_t base; int have_signals; int nevents; @@ -262,11 +269,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->events = w->pevents; } - psigset = NULL; + sigmask = 0; if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { sigemptyset(&sigset); sigaddset(&sigset, SIGPROF); - psigset = &sigset; + sigmask |= 1 << (SIGPROF - 1); } assert(timeout >= -1); @@ -281,11 +288,35 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) timeout = max_safe_timeout; - nfds = epoll_pwait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout, - psigset); + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) + abort(); + + if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) { +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 + nfds = -1; + errno = ENOSYS; +#else + nfds = epoll_pwait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout, + &sigset); +#endif + if (nfds == -1 && errno == ENOSYS) + no_epoll_pwait = 1; + } else { + nfds = epoll_wait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout); + if (nfds == -1 && errno == ENOSYS) + no_epoll_wait = 1; + } + + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) + abort(); /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the @@ -306,6 +337,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } if (nfds == -1) { + if (errno == ENOSYS) { + /* epoll_wait() or epoll_pwait() failed, try the other system call. */ + assert(no_epoll_wait == 0 || no_epoll_pwait == 0); + continue; + } + if (errno != EINTR) abort(); @@ -322,9 +359,19 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { have_signals = 0; nevents = 0; - assert(loop->watchers != NULL); - loop->watchers[loop->nwatchers] = (void*) events; - loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + { + /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */ + union { + struct epoll_event* events; + uv__io_t* watchers; + } x; + + x.events = events; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = x.watchers; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + } + for (i = 0; i < nfds; i++) { pe = events + i; fd = pe->data.fd; @@ -721,7 +768,8 @@ static int read_times(FILE* statfile_fp, unsigned int numcpus, uv_cpu_info_t* ci) { struct uv_cpu_times_s ts; - uint64_t clock_ticks; + unsigned int ticks; + unsigned int multiplier; uint64_t user; uint64_t nice; uint64_t sys; @@ -732,9 +780,10 @@ static int read_times(FILE* statfile_fp, uint64_t len; char buf[1024]; - clock_ticks = sysconf(_SC_CLK_TCK); - assert(clock_ticks != (uint64_t) -1); - assert(clock_ticks != 0); + ticks = (unsigned int)sysconf(_SC_CLK_TCK); + multiplier = ((uint64_t)1000L / ticks); + assert(ticks != (unsigned int) -1); + assert(ticks != 0); rewind(statfile_fp); @@ -776,11 +825,11 @@ static int read_times(FILE* statfile_fp, &irq)) abort(); - ts.user = clock_ticks * user; - ts.nice = clock_ticks * nice; - ts.sys = clock_ticks * sys; - ts.idle = clock_ticks * idle; - ts.irq = clock_ticks * irq; + ts.user = user * multiplier; + ts.nice = nice * multiplier; + ts.sys = sys * multiplier; + ts.idle = idle * multiplier; + ts.irq = irq * multiplier; ci[num++].cpu_times = ts; } assert(num == numcpus); @@ -812,16 +861,6 @@ static uint64_t read_cpufreq(unsigned int cpunum) { } -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; - - for (i = 0; i < count; i++) { - uv__free(cpu_infos[i].model); - } - - uv__free(cpu_infos); -} - static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) return 1; @@ -953,7 +992,7 @@ static uint64_t uv__read_proc_meminfo(const char* what) { rc = 0; fd = uv__open_cloexec("/proc/meminfo", O_RDONLY); - if (fd == -1) + if (fd < 0) return 0; n = read(fd, buf, sizeof(buf) - 1); diff --git a/Utilities/cmlibuv/src/unix/linux-inotify.c b/Utilities/cmlibuv/src/unix/linux-inotify.c index 9b26202fb33..42b601adbf8 100644 --- a/Utilities/cmlibuv/src/unix/linux-inotify.c +++ b/Utilities/cmlibuv/src/unix/linux-inotify.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -64,45 +65,17 @@ static void uv__inotify_read(uv_loop_t* loop, static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop); -static int new_inotify_fd(void) { - int err; - int fd; - - fd = uv__inotify_init1(UV__IN_NONBLOCK | UV__IN_CLOEXEC); - if (fd != -1) - return fd; - - if (errno != ENOSYS) - return UV__ERR(errno); - - fd = uv__inotify_init(); - if (fd == -1) - return UV__ERR(errno); - - err = uv__cloexec(fd, 1); - if (err == 0) - err = uv__nonblock(fd, 1); - - if (err) { - uv__close(fd); - return err; - } - - return fd; -} - - static int init_inotify(uv_loop_t* loop) { - int err; + int fd; if (loop->inotify_fd != -1) return 0; - err = new_inotify_fd(); - if (err < 0) - return err; + fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd < 0) + return UV__ERR(errno); - loop->inotify_fd = err; + loop->inotify_fd = fd; uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd); uv__io_start(loop, &loop->inotify_read_watcher, POLLIN); @@ -186,7 +159,7 @@ static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) { if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) { /* No watchers left for this path. Clean up. */ RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w); - uv__inotify_rm_watch(loop->inotify_fd, w->wd); + inotify_rm_watch(loop->inotify_fd, w->wd); uv__free(w); } } @@ -194,7 +167,7 @@ static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) { static void uv__inotify_read(uv_loop_t* loop, uv__io_t* dummy, unsigned int events) { - const struct uv__inotify_event* e; + const struct inotify_event* e; struct watcher_list* w; uv_fs_event_t* h; QUEUE queue; @@ -219,12 +192,12 @@ static void uv__inotify_read(uv_loop_t* loop, /* Now we have one or more inotify_event structs. */ for (p = buf; p < buf + size; p += sizeof(*e) + e->len) { - e = (const struct uv__inotify_event*)p; + e = (const struct inotify_event*) p; events = 0; - if (e->mask & (UV__IN_ATTRIB|UV__IN_MODIFY)) + if (e->mask & (IN_ATTRIB|IN_MODIFY)) events |= UV_CHANGE; - if (e->mask & ~(UV__IN_ATTRIB|UV__IN_MODIFY)) + if (e->mask & ~(IN_ATTRIB|IN_MODIFY)) events |= UV_RENAME; w = find_watcher(loop, e->wd); @@ -290,16 +263,16 @@ int uv_fs_event_start(uv_fs_event_t* handle, if (err) return err; - events = UV__IN_ATTRIB - | UV__IN_CREATE - | UV__IN_MODIFY - | UV__IN_DELETE - | UV__IN_DELETE_SELF - | UV__IN_MOVE_SELF - | UV__IN_MOVED_FROM - | UV__IN_MOVED_TO; + events = IN_ATTRIB + | IN_CREATE + | IN_MODIFY + | IN_DELETE + | IN_DELETE_SELF + | IN_MOVE_SELF + | IN_MOVED_FROM + | IN_MOVED_TO; - wd = uv__inotify_add_watch(handle->loop->inotify_fd, path, events); + wd = inotify_add_watch(handle->loop->inotify_fd, path, events); if (wd == -1) return UV__ERR(errno); diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.c b/Utilities/cmlibuv/src/unix/linux-syscalls.c index 5637cf98a7b..742f26ada82 100644 --- a/Utilities/cmlibuv/src/unix/linux-syscalls.c +++ b/Utilities/cmlibuv/src/unix/linux-syscalls.c @@ -26,19 +26,6 @@ #include #include -#if defined(__has_feature) -# if __has_feature(memory_sanitizer) -# define MSAN_ACTIVE 1 -# include -# endif -#endif - -#if defined(__i386__) -# ifndef __NR_socketcall -# define __NR_socketcall 102 -# endif -#endif - #if defined(__arm__) # if defined(__thumb__) || defined(__ARM_EABI__) # define UV_SYSCALL_BASE 0 @@ -47,86 +34,6 @@ # endif #endif /* __arm__ */ -#ifndef __NR_accept4 -# if defined(__x86_64__) -# define __NR_accept4 288 -# elif defined(__i386__) - /* Nothing. Handled through socketcall(). */ -# elif defined(__arm__) -# define __NR_accept4 (UV_SYSCALL_BASE + 366) -# endif -#endif /* __NR_accept4 */ - -#ifndef __NR_eventfd -# if defined(__x86_64__) -# define __NR_eventfd 284 -# elif defined(__i386__) -# define __NR_eventfd 323 -# elif defined(__arm__) -# define __NR_eventfd (UV_SYSCALL_BASE + 351) -# endif -#endif /* __NR_eventfd */ - -#ifndef __NR_eventfd2 -# if defined(__x86_64__) -# define __NR_eventfd2 290 -# elif defined(__i386__) -# define __NR_eventfd2 328 -# elif defined(__arm__) -# define __NR_eventfd2 (UV_SYSCALL_BASE + 356) -# endif -#endif /* __NR_eventfd2 */ - -#ifndef __NR_inotify_init -# if defined(__x86_64__) -# define __NR_inotify_init 253 -# elif defined(__i386__) -# define __NR_inotify_init 291 -# elif defined(__arm__) -# define __NR_inotify_init (UV_SYSCALL_BASE + 316) -# endif -#endif /* __NR_inotify_init */ - -#ifndef __NR_inotify_init1 -# if defined(__x86_64__) -# define __NR_inotify_init1 294 -# elif defined(__i386__) -# define __NR_inotify_init1 332 -# elif defined(__arm__) -# define __NR_inotify_init1 (UV_SYSCALL_BASE + 360) -# endif -#endif /* __NR_inotify_init1 */ - -#ifndef __NR_inotify_add_watch -# if defined(__x86_64__) -# define __NR_inotify_add_watch 254 -# elif defined(__i386__) -# define __NR_inotify_add_watch 292 -# elif defined(__arm__) -# define __NR_inotify_add_watch (UV_SYSCALL_BASE + 317) -# endif -#endif /* __NR_inotify_add_watch */ - -#ifndef __NR_inotify_rm_watch -# if defined(__x86_64__) -# define __NR_inotify_rm_watch 255 -# elif defined(__i386__) -# define __NR_inotify_rm_watch 293 -# elif defined(__arm__) -# define __NR_inotify_rm_watch (UV_SYSCALL_BASE + 318) -# endif -#endif /* __NR_inotify_rm_watch */ - -#ifndef __NR_pipe2 -# if defined(__x86_64__) -# define __NR_pipe2 293 -# elif defined(__i386__) -# define __NR_pipe2 331 -# elif defined(__arm__) -# define __NR_pipe2 (UV_SYSCALL_BASE + 359) -# endif -#endif /* __NR_pipe2 */ - #ifndef __NR_recvmmsg # if defined(__x86_64__) # define __NR_recvmmsg 299 @@ -203,103 +110,23 @@ # endif #endif /* __NR_statx */ -int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) { -#if defined(__i386__) - unsigned long args[4]; - int r; - - args[0] = (unsigned long) fd; - args[1] = (unsigned long) addr; - args[2] = (unsigned long) addrlen; - args[3] = (unsigned long) flags; - - r = syscall(__NR_socketcall, 18 /* SYS_ACCEPT4 */, args); - - /* socketcall() raises EINVAL when SYS_ACCEPT4 is not supported but so does - * a bad flags argument. Try to distinguish between the two cases. - */ - if (r == -1) - if (errno == EINVAL) - if ((flags & ~(UV__SOCK_CLOEXEC|UV__SOCK_NONBLOCK)) == 0) - errno = ENOSYS; - - return r; -#elif defined(__NR_accept4) - return syscall(__NR_accept4, fd, addr, addrlen, flags); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__eventfd(unsigned int count) { -#if defined(__NR_eventfd) - return syscall(__NR_eventfd, count); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__eventfd2(unsigned int count, int flags) { -#if defined(__NR_eventfd2) - return syscall(__NR_eventfd2, count, flags); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__inotify_init(void) { -#if defined(__NR_inotify_init) - return syscall(__NR_inotify_init); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__inotify_init1(int flags) { -#if defined(__NR_inotify_init1) - return syscall(__NR_inotify_init1, flags); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__inotify_add_watch(int fd, const char* path, uint32_t mask) { -#if defined(__NR_inotify_add_watch) - return syscall(__NR_inotify_add_watch, fd, path, mask); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__inotify_rm_watch(int fd, int32_t wd) { -#if defined(__NR_inotify_rm_watch) - return syscall(__NR_inotify_rm_watch, fd, wd); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__pipe2(int pipefd[2], int flags) { -#if defined(__NR_pipe2) - int result; - result = syscall(__NR_pipe2, pipefd, flags); -#if MSAN_ACTIVE - if (!result) - __msan_unpoison(pipefd, sizeof(int[2])); -#endif - return result; -#else - return errno = ENOSYS, -1; -#endif -} +#ifndef __NR_getrandom +# if defined(__x86_64__) +# define __NR_getrandom 318 +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__aarch64__) +# define __NR_getrandom 384 +# elif defined(__arm__) +# define __NR_getrandom (UV_SYSCALL_BASE + 384) +# elif defined(__ppc__) +# define __NR_getrandom 359 +# elif defined(__s390__) +# define __NR_getrandom 349 +# endif +#endif /* __NR_getrandom */ +struct uv__mmsghdr; int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, @@ -367,3 +194,12 @@ int uv__statx(int dirfd, return errno = ENOSYS, -1; #endif } + + +ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) { +#if defined(__NR_getrandom) + return syscall(__NR_getrandom, buf, buflen, flags); +#else + return errno = ENOSYS, -1; +#endif +} diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.h b/Utilities/cmlibuv/src/unix/linux-syscalls.h index 7e58bfa2189..2e8fa2a5197 100644 --- a/Utilities/cmlibuv/src/unix/linux-syscalls.h +++ b/Utilities/cmlibuv/src/unix/linux-syscalls.h @@ -31,55 +31,6 @@ #include #include -#if defined(__alpha__) -# define UV__O_CLOEXEC 0x200000 -#elif defined(__hppa__) -# define UV__O_CLOEXEC 0x200000 -#elif defined(__sparc__) -# define UV__O_CLOEXEC 0x400000 -#else -# define UV__O_CLOEXEC 0x80000 -#endif - -#if defined(__alpha__) -# define UV__O_NONBLOCK 0x4 -#elif defined(__hppa__) -# define UV__O_NONBLOCK O_NONBLOCK -#elif defined(__mips__) -# define UV__O_NONBLOCK 0x80 -#elif defined(__sparc__) -# define UV__O_NONBLOCK 0x4000 -#else -# define UV__O_NONBLOCK 0x800 -#endif - -#define UV__EFD_CLOEXEC UV__O_CLOEXEC -#define UV__EFD_NONBLOCK UV__O_NONBLOCK - -#define UV__IN_CLOEXEC UV__O_CLOEXEC -#define UV__IN_NONBLOCK UV__O_NONBLOCK - -#define UV__SOCK_CLOEXEC UV__O_CLOEXEC -#if defined(SOCK_NONBLOCK) -# define UV__SOCK_NONBLOCK SOCK_NONBLOCK -#else -# define UV__SOCK_NONBLOCK UV__O_NONBLOCK -#endif - -/* inotify flags */ -#define UV__IN_ACCESS 0x001 -#define UV__IN_MODIFY 0x002 -#define UV__IN_ATTRIB 0x004 -#define UV__IN_CLOSE_WRITE 0x008 -#define UV__IN_CLOSE_NOWRITE 0x010 -#define UV__IN_OPEN 0x020 -#define UV__IN_MOVED_FROM 0x040 -#define UV__IN_MOVED_TO 0x080 -#define UV__IN_CREATE 0x100 -#define UV__IN_DELETE 0x200 -#define UV__IN_DELETE_SELF 0x400 -#define UV__IN_MOVE_SELF 0x800 - struct uv__statx_timestamp { int64_t tv_sec; uint32_t tv_nsec; @@ -110,36 +61,6 @@ struct uv__statx { uint64_t unused1[14]; }; -struct uv__inotify_event { - int32_t wd; - uint32_t mask; - uint32_t cookie; - uint32_t len; - /* char name[0]; */ -}; - -struct uv__mmsghdr { - struct msghdr msg_hdr; - unsigned int msg_len; -}; - -int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags); -int uv__eventfd(unsigned int count); -int uv__eventfd2(unsigned int count, int flags); -int uv__inotify_init(void); -int uv__inotify_init1(int flags); -int uv__inotify_add_watch(int fd, const char* path, uint32_t mask); -int uv__inotify_rm_watch(int fd, int32_t wd); -int uv__pipe2(int pipefd[2], int flags); -int uv__recvmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags, - struct timespec* timeout); -int uv__sendmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags); ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset); ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset); int uv__dup3(int oldfd, int newfd, int flags); @@ -148,5 +69,6 @@ int uv__statx(int dirfd, int flags, unsigned int mask, struct uv__statx* statxbuf); +ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags); #endif /* UV_LINUX_SYSCALL_H_ */ diff --git a/Utilities/cmlibuv/src/unix/netbsd.c b/Utilities/cmlibuv/src/unix/netbsd.c index c649bb375f3..c66333f522c 100644 --- a/Utilities/cmlibuv/src/unix/netbsd.c +++ b/Utilities/cmlibuv/src/unix/netbsd.c @@ -55,7 +55,7 @@ void uv_loadavg(double avg[3]) { size_t size = sizeof(info); int which[] = {CTL_VM, VM_LOADAVG}; - if (sysctl(which, 2, &info, &size, NULL, 0) == -1) return; + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) == -1) return; avg[0] = (double) info.ldavg[0] / info.fscale; avg[1] = (double) info.ldavg[1] / info.fscale; @@ -102,7 +102,7 @@ uint64_t uv_get_free_memory(void) { size_t size = sizeof(info); int which[] = {CTL_VM, VM_UVMEXP}; - if (sysctl(which, 2, &info, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) return UV__ERR(errno); return (uint64_t) info.free * sysconf(_SC_PAGESIZE); @@ -119,7 +119,7 @@ uint64_t uv_get_total_memory(void) { #endif size_t size = sizeof(info); - if (sysctl(which, 2, &info, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) return UV__ERR(errno); return (uint64_t) info; @@ -167,7 +167,7 @@ int uv_uptime(double* uptime) { size_t size = sizeof(info); static int which[] = {CTL_KERN, KERN_BOOTTIME}; - if (sysctl(which, 2, &info, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) return UV__ERR(errno); now = time(NULL); @@ -235,13 +235,25 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return 0; } +int uv__random_sysctl(void* buf, size_t len) { + static int name[] = {CTL_KERN, KERN_ARND}; + size_t count, req; + unsigned char* p; -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; + p = buf; + while (len) { + req = len < 32 ? len : 32; + count = req; + + if (sysctl(name, ARRAY_SIZE(name), p, &count, NULL, 0) == -1) + return UV__ERR(errno); - for (i = 0; i < count; i++) { - uv__free(cpu_infos[i].model); + if (count != req) + return UV_EIO; /* Can't happen. */ + + p += count; + len -= count; } - uv__free(cpu_infos); + return 0; } diff --git a/Utilities/cmlibuv/src/unix/no-proctitle.c b/Utilities/cmlibuv/src/unix/no-proctitle.c index 165740ca3ff..32aa0af1f92 100644 --- a/Utilities/cmlibuv/src/unix/no-proctitle.c +++ b/Utilities/cmlibuv/src/unix/no-proctitle.c @@ -29,6 +29,9 @@ char** uv_setup_args(int argc, char** argv) { return argv; } +void uv__process_title_cleanup(void) { +} + int uv_set_process_title(const char* title) { return 0; } diff --git a/Utilities/cmlibuv/src/unix/openbsd.c b/Utilities/cmlibuv/src/unix/openbsd.c index ffae7683d8b..f32a94df387 100644 --- a/Utilities/cmlibuv/src/unix/openbsd.c +++ b/Utilities/cmlibuv/src/unix/openbsd.c @@ -50,7 +50,7 @@ void uv_loadavg(double avg[3]) { size_t size = sizeof(info); int which[] = {CTL_VM, VM_LOADAVG}; - if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return; + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return; avg[0] = (double) info.ldavg[0] / info.fscale; avg[1] = (double) info.ldavg[1] / info.fscale; @@ -61,7 +61,6 @@ void uv_loadavg(double avg[3]) { int uv_exepath(char* buffer, size_t* size) { int mib[4]; char **argsbuf = NULL; - char **argsbuf_tmp; size_t argsbuf_size = 100U; size_t exepath_size; pid_t mypid; @@ -73,15 +72,14 @@ int uv_exepath(char* buffer, size_t* size) { mypid = getpid(); for (;;) { err = UV_ENOMEM; - argsbuf_tmp = uv__realloc(argsbuf, argsbuf_size); - if (argsbuf_tmp == NULL) + argsbuf = uv__reallocf(argsbuf, argsbuf_size); + if (argsbuf == NULL) goto out; - argsbuf = argsbuf_tmp; mib[0] = CTL_KERN; mib[1] = KERN_PROC_ARGS; mib[2] = mypid; mib[3] = KERN_PROC_ARGV; - if (sysctl(mib, 4, argsbuf, &argsbuf_size, NULL, 0) == 0) { + if (sysctl(mib, ARRAY_SIZE(mib), argsbuf, &argsbuf_size, NULL, 0) == 0) { break; } if (errno != ENOMEM) { @@ -117,7 +115,7 @@ uint64_t uv_get_free_memory(void) { size_t size = sizeof(info); int which[] = {CTL_VM, VM_UVMEXP}; - if (sysctl(which, 2, &info, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) return UV__ERR(errno); return (uint64_t) info.free * sysconf(_SC_PAGESIZE); @@ -129,7 +127,7 @@ uint64_t uv_get_total_memory(void) { int which[] = {CTL_HW, HW_PHYSMEM64}; size_t size = sizeof(info); - if (sysctl(which, 2, &info, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) return UV__ERR(errno); return (uint64_t) info; @@ -154,7 +152,7 @@ int uv_resident_set_memory(size_t* rss) { mib[4] = sizeof(struct kinfo_proc); mib[5] = 1; - if (sysctl(mib, 6, &kinfo, &size, NULL, 0) < 0) + if (sysctl(mib, ARRAY_SIZE(mib), &kinfo, &size, NULL, 0) < 0) return UV__ERR(errno); *rss = kinfo.p_vm_rssize * page_size; @@ -168,7 +166,7 @@ int uv_uptime(double* uptime) { size_t size = sizeof(info); static int which[] = {CTL_KERN, KERN_BOOTTIME}; - if (sysctl(which, 2, &info, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) return UV__ERR(errno); now = time(NULL); @@ -184,43 +182,38 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { uint64_t info[CPUSTATES]; char model[512]; int numcpus = 1; - int which[] = {CTL_HW,HW_MODEL,0}; + int which[] = {CTL_HW,HW_MODEL}; + int percpu[] = {CTL_KERN,KERN_CPTIME2,0}; size_t size; - int i; + int i, j; uv_cpu_info_t* cpu_info; size = sizeof(model); - if (sysctl(which, 2, &model, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &model, &size, NULL, 0)) return UV__ERR(errno); - which[1] = HW_NCPU; + which[1] = HW_NCPUONLINE; size = sizeof(numcpus); - if (sysctl(which, 2, &numcpus, &size, NULL, 0)) + if (sysctl(which, ARRAY_SIZE(which), &numcpus, &size, NULL, 0)) return UV__ERR(errno); *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); if (!(*cpu_infos)) return UV_ENOMEM; + i = 0; *count = numcpus; which[1] = HW_CPUSPEED; size = sizeof(cpuspeed); - if (sysctl(which, 2, &cpuspeed, &size, NULL, 0)) { - uv__free(*cpu_infos); - return UV__ERR(errno); - } + if (sysctl(which, ARRAY_SIZE(which), &cpuspeed, &size, NULL, 0)) + goto error; size = sizeof(info); - which[0] = CTL_KERN; - which[1] = KERN_CPTIME2; for (i = 0; i < numcpus; i++) { - which[2] = i; - size = sizeof(info); - if (sysctl(which, 3, &info, &size, NULL, 0)) { - uv__free(*cpu_infos); - return UV__ERR(errno); - } + percpu[2] = i; + if (sysctl(percpu, ARRAY_SIZE(percpu), &info, &size, NULL, 0)) + goto error; cpu_info = &(*cpu_infos)[i]; @@ -235,15 +228,13 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } return 0; -} +error: + *count = 0; + for (j = 0; j < i; j++) + uv__free((*cpu_infos)[j].model); -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; - - for (i = 0; i < count; i++) { - uv__free(cpu_infos[i].model); - } - - uv__free(cpu_infos); + uv__free(*cpu_infos); + *cpu_infos = NULL; + return UV__ERR(errno); } diff --git a/Utilities/cmlibuv/src/unix/os390-syscalls.c b/Utilities/cmlibuv/src/unix/os390-syscalls.c index 1040d66979d..424cc486a86 100644 --- a/Utilities/cmlibuv/src/unix/os390-syscalls.c +++ b/Utilities/cmlibuv/src/unix/os390-syscalls.c @@ -23,11 +23,11 @@ #include "os390-syscalls.h" #include #include -#include #include #include #include +#define CW_INTRPT 1 #define CW_CONDVAR 32 #pragma linkage(BPX4CTW, OS) @@ -43,6 +43,7 @@ int scandir(const char* maindir, struct dirent*** namelist, int (*compar)(const struct dirent**, const struct dirent **)) { struct dirent** nl; + struct dirent** nl_copy; struct dirent* dirent; unsigned count; size_t allocated; @@ -62,19 +63,17 @@ int scandir(const char* maindir, struct dirent*** namelist, if (!filter || filter(dirent)) { struct dirent* copy; copy = uv__malloc(sizeof(*copy)); - if (!copy) { - while (count) { - dirent = nl[--count]; - uv__free(dirent); - } - uv__free(nl); - closedir(mdir); - errno = ENOMEM; - return -1; - } + if (!copy) + goto error; memcpy(copy, dirent, sizeof(*copy)); - nl = uv__realloc(nl, sizeof(*copy) * (count + 1)); + nl_copy = uv__realloc(nl, sizeof(*copy) * (count + 1)); + if (nl_copy == NULL) { + uv__free(copy); + goto error; + } + + nl = nl_copy; nl[count++] = copy; } } @@ -86,6 +85,16 @@ int scandir(const char* maindir, struct dirent*** namelist, *namelist = nl; return count; + +error: + while (count > 0) { + dirent = nl[--count]; + uv__free(dirent); + } + uv__free(nl); + closedir(mdir); + errno = ENOMEM; + return -1; } @@ -119,7 +128,7 @@ static void maybe_resize(uv__os390_epoll* lst, unsigned int len) { } newsize = next_power_of_two(len); - newlst = uv__realloc(lst->items, newsize * sizeof(lst->items[0])); + newlst = uv__reallocf(lst->items, newsize * sizeof(lst->items[0])); if (newlst == NULL) abort(); @@ -269,6 +278,8 @@ int epoll_ctl(uv__os390_epoll* lst, return 0; } +#define EP_MAX_PFDS (ULONG_MAX / sizeof(struct pollfd)) +#define EP_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event)) int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, int maxevents, int timeout) { @@ -277,18 +288,41 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, int pollret; int reventcount; int nevents; + struct pollfd msg_fd; + int i; - _SET_FDS_MSGS(size, 1, lst->size - 1); + if (!lst || !lst->items || !events) { + errno = EFAULT; + return -1; + } + + if (lst->size > EP_MAX_PFDS) { + errno = EINVAL; + return -1; + } + + if (maxevents <= 0 || maxevents > EP_MAX_EVENTS) { + errno = EINVAL; + return -1; + } + + if (lst->size > 0) + _SET_FDS_MSGS(size, 1, lst->size - 1); + else + _SET_FDS_MSGS(size, 0, 0); pfds = lst->items; pollret = poll(pfds, size, timeout); if (pollret <= 0) return pollret; + assert(lst->size > 0); + pollret = _NFDS(pollret) + _NMSGS(pollret); reventcount = 0; nevents = 0; - for (int i = 0; + msg_fd = pfds[lst->size - 1]; + for (i = 0; i < lst->size && i < maxevents && reventcount < pollret; ++i) { struct epoll_event ev; struct pollfd* pfd; @@ -299,6 +333,7 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, ev.fd = pfd->fd; ev.events = pfd->revents; + ev.is_msg = 0; if (pfd->revents & POLLIN && pfd->revents & POLLOUT) reventcount += 2; else if (pfd->revents & (POLLIN | POLLOUT)) @@ -308,6 +343,10 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, events[nevents++] = ev; } + if (msg_fd.revents != 0 && msg_fd.fd != -1) + if (i == lst->size) + events[nevents - 1].is_msg = 1; + return nevents; } @@ -350,27 +389,36 @@ int nanosleep(const struct timespec* req, struct timespec* rem) { unsigned secrem; unsigned nanorem; int rv; - int rc; + int err; int rsn; nano = (int)req->tv_nsec; seconds = req->tv_sec; - events = CW_CONDVAR; + events = CW_CONDVAR | CW_INTRPT; + secrem = 0; + nanorem = 0; #if defined(_LP64) - BPX4CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &rc, &rsn); + BPX4CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn); #else - BPX1CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &rc, &rsn); + BPX1CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn); #endif - assert(rv == -1 && errno == EAGAIN); + /* Don't clobber errno unless BPX1CTW/BPX4CTW errored. + * Don't leak EAGAIN, that just means the timeout expired. + */ + if (rv == -1) + if (err == EAGAIN) + rv = 0; + else + errno = err; - if(rem != NULL) { + if (rem != NULL && (rv == 0 || err == EINTR)) { rem->tv_nsec = nanorem; rem->tv_sec = secrem; } - return 0; + return rv; } @@ -510,3 +558,28 @@ size_t strnlen(const char* str, size_t maxlen) { else return p - str; } + + +int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value) { + UNREACHABLE(); +} + + +int sem_destroy(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} + + +int sem_post(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} + + +int sem_trywait(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} + + +int sem_wait(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} diff --git a/Utilities/cmlibuv/src/unix/os390-syscalls.h b/Utilities/cmlibuv/src/unix/os390-syscalls.h index ea599107b30..86416bbc551 100644 --- a/Utilities/cmlibuv/src/unix/os390-syscalls.h +++ b/Utilities/cmlibuv/src/unix/os390-syscalls.h @@ -40,6 +40,7 @@ struct epoll_event { int events; int fd; + int is_msg; }; typedef struct { @@ -64,5 +65,10 @@ int scandir(const char* maindir, struct dirent*** namelist, char *mkdtemp(char* path); ssize_t os390_readlink(const char* path, char* buf, size_t len); size_t strnlen(const char* str, size_t maxlen); +int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value); +int sem_destroy(UV_PLATFORM_SEM_T* semid); +int sem_post(UV_PLATFORM_SEM_T* semid); +int sem_trywait(UV_PLATFORM_SEM_T* semid); +int sem_wait(UV_PLATFORM_SEM_T* semid); #endif /* UV_OS390_SYSCALL_H_ */ diff --git a/Utilities/cmlibuv/src/unix/os390.c b/Utilities/cmlibuv/src/unix/os390.c index 273ded7ca5e..dce169b9fb4 100644 --- a/Utilities/cmlibuv/src/unix/os390.c +++ b/Utilities/cmlibuv/src/unix/os390.c @@ -433,13 +433,6 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - for (int i = 0; i < count; ++i) - uv__free(cpu_infos[i].model); - uv__free(cpu_infos); -} - - static int uv__interface_addresses_v6(uv_interface_address_t** addresses, int* count) { uv_interface_address_t* address; @@ -930,7 +923,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { continue; ep = loop->ep; - if (fd == ep->msg_queue) { + if (pe->is_msg) { os390_message_queue_handler(ep); continue; } diff --git a/Utilities/cmlibuv/src/unix/pipe.c b/Utilities/cmlibuv/src/unix/pipe.c index 7d97550ae24..52a8fd52fac 100644 --- a/Utilities/cmlibuv/src/unix/pipe.c +++ b/Utilities/cmlibuv/src/unix/pipe.c @@ -93,8 +93,12 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { if (uv__stream_fd(handle) == -1) return UV_EINVAL; -#if defined(__MVS__) + if (handle->ipc) + return UV_EINVAL; + +#if defined(__MVS__) || defined(__PASE__) /* On zOS, backlog=0 has undefined behaviour */ + /* On IBMi PASE, backlog=0 leads to "Connection refused" error */ if (backlog == 0) backlog = 1; else if (backlog < 0) @@ -259,7 +263,7 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, addrlen = strlen(sa.sun_path); - if (addrlen >= *size) { + if ((size_t)addrlen >= *size) { *size = addrlen + 1; return UV_ENOBUFS; } diff --git a/Utilities/cmlibuv/src/unix/posix-poll.c b/Utilities/cmlibuv/src/unix/posix-poll.c index a3b9f2196d5..766e83205d0 100644 --- a/Utilities/cmlibuv/src/unix/posix-poll.c +++ b/Utilities/cmlibuv/src/unix/posix-poll.c @@ -61,7 +61,7 @@ static void uv__pollfds_maybe_resize(uv_loop_t* loop) { return; n = loop->poll_fds_size ? loop->poll_fds_size * 2 : 64; - p = uv__realloc(loop->poll_fds, n * sizeof(*loop->poll_fds)); + p = uv__reallocf(loop->poll_fds, n * sizeof(*loop->poll_fds)); if (p == NULL) abort(); diff --git a/Utilities/cmlibuv/src/unix/process.c b/Utilities/cmlibuv/src/unix/process.c index f4826bf62a6..08aa2f3cc6a 100644 --- a/Utilities/cmlibuv/src/unix/process.c +++ b/Utilities/cmlibuv/src/unix/process.c @@ -123,72 +123,64 @@ static void uv__chld(uv_signal_t* handle, int signum) { } -int uv__make_socketpair(int fds[2], int flags) { -#if defined(__linux__) - static int no_cloexec; - - if (no_cloexec) - goto skip; - - if (socketpair(AF_UNIX, SOCK_STREAM | UV__SOCK_CLOEXEC | flags, 0, fds) == 0) - return 0; - - /* Retry on EINVAL, it means SOCK_CLOEXEC is not supported. - * Anything else is a genuine error. - */ - if (errno != EINVAL) +static int uv__make_socketpair(int fds[2]) { +#if defined(__FreeBSD__) || defined(__linux__) + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds)) return UV__ERR(errno); - no_cloexec = 1; - -skip: -#endif + return 0; +#else + int err; if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) return UV__ERR(errno); - uv__cloexec(fds[0], 1); - uv__cloexec(fds[1], 1); + err = uv__cloexec(fds[0], 1); + if (err == 0) + err = uv__cloexec(fds[1], 1); - if (flags & UV__F_NONBLOCK) { - uv__nonblock(fds[0], 1); - uv__nonblock(fds[1], 1); + if (err != 0) { + uv__close(fds[0]); + uv__close(fds[1]); + return UV__ERR(errno); } return 0; +#endif } int uv__make_pipe(int fds[2], int flags) { -#if defined(__linux__) - static int no_pipe2; - - if (no_pipe2) - goto skip; - - if (uv__pipe2(fds, flags | UV__O_CLOEXEC) == 0) - return 0; - - if (errno != ENOSYS) +#if defined(__FreeBSD__) || defined(__linux__) + if (pipe2(fds, flags | O_CLOEXEC)) return UV__ERR(errno); - no_pipe2 = 1; - -skip: -#endif - + return 0; +#else if (pipe(fds)) return UV__ERR(errno); - uv__cloexec(fds[0], 1); - uv__cloexec(fds[1], 1); + if (uv__cloexec(fds[0], 1)) + goto fail; + + if (uv__cloexec(fds[1], 1)) + goto fail; if (flags & UV__F_NONBLOCK) { - uv__nonblock(fds[0], 1); - uv__nonblock(fds[1], 1); + if (uv__nonblock(fds[0], 1)) + goto fail; + + if (uv__nonblock(fds[1], 1)) + goto fail; } return 0; + +fail: + uv__close(fds[0]); + uv__close(fds[1]); + return UV__ERR(errno); +#endif } @@ -211,7 +203,7 @@ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { if (container->data.stream->type != UV_NAMED_PIPE) return UV_EINVAL; else - return uv__make_socketpair(fds, 0); + return uv__make_socketpair(fds); case UV_INHERIT_FD: case UV_INHERIT_STREAM: @@ -260,7 +252,7 @@ static int uv__process_open_stream(uv_stdio_container_t* container, static void uv__process_close_stream(uv_stdio_container_t* container) { if (!(container->flags & UV_CREATE_PIPE)) return; - uv__stream_close((uv_stream_t*)container->data.stream); + uv__stream_close(container->data.stream); } diff --git a/Utilities/cmlibuv/src/unix/proctitle.c b/Utilities/cmlibuv/src/unix/proctitle.c index a5ce2030c55..4ee991fcc32 100644 --- a/Utilities/cmlibuv/src/unix/proctitle.c +++ b/Utilities/cmlibuv/src/unix/proctitle.c @@ -24,28 +24,27 @@ #include #include -extern void uv__set_process_title_platform_init(void); +struct uv__process_title { + char* str; + size_t len; /* Length of the current process title. */ + size_t cap; /* Maximum capacity. Computed once in uv_setup_args(). */ +}; + extern void uv__set_process_title(const char* title); static uv_mutex_t process_title_mutex; static uv_once_t process_title_mutex_once = UV_ONCE_INIT; +static struct uv__process_title process_title; static void* args_mem; -static struct { - char* str; - size_t len; -} process_title; - static void init_process_title_mutex_once(void) { uv_mutex_init(&process_title_mutex); -#ifdef __APPLE__ - uv__set_process_title_platform_init(); -#endif } char** uv_setup_args(int argc, char** argv) { + struct uv__process_title pt; char** new_argv; size_t size; char* s; @@ -54,53 +53,69 @@ char** uv_setup_args(int argc, char** argv) { if (argc <= 0) return argv; + pt.str = argv[0]; + pt.len = strlen(argv[0]); + pt.cap = pt.len + 1; + /* Calculate how much memory we need for the argv strings. */ - size = 0; - for (i = 0; i < argc; i++) + size = pt.cap; + for (i = 1; i < argc; i++) size += strlen(argv[i]) + 1; -#if defined(__MVS__) - /* argv is not adjacent. So just use argv[0] */ - process_title.str = argv[0]; - process_title.len = strlen(argv[0]); -#else - process_title.str = argv[0]; - process_title.len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[0]; - assert(process_title.len + 1 == size); /* argv memory should be adjacent. */ -#endif - /* Add space for the argv pointers. */ size += (argc + 1) * sizeof(char*); new_argv = uv__malloc(size); if (new_argv == NULL) return argv; - args_mem = new_argv; /* Copy over the strings and set up the pointer table. */ + i = 0; s = (char*) &new_argv[argc + 1]; - for (i = 0; i < argc; i++) { + size = pt.cap; + goto loop; + + for (/* empty */; i < argc; i++) { size = strlen(argv[i]) + 1; + loop: memcpy(s, argv[i], size); new_argv[i] = s; s += size; } new_argv[i] = NULL; + /* argv is not adjacent on z/os, we use just argv[0] on that platform. */ +#ifndef __MVS__ + pt.cap = argv[i - 1] + size - argv[0]; +#endif + + args_mem = new_argv; + process_title = pt; + return new_argv; } int uv_set_process_title(const char* title) { + struct uv__process_title* pt; + size_t len; + + pt = &process_title; + len = strlen(title); + uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); - if (process_title.len != 0) { - /* No need to terminate, byte after is always '\0'. */ - strncpy(process_title.str, title, process_title.len); - uv__set_process_title(title); + if (len >= pt->cap) { + len = 0; + if (pt->cap > 0) + len = pt->cap - 1; } + memcpy(pt->str, title, len); + memset(pt->str + len, '\0', pt->cap - len); + pt->len = len; + uv_mutex_unlock(&process_title_mutex); return 0; @@ -130,7 +145,7 @@ int uv_get_process_title(char* buffer, size_t size) { } -UV_DESTRUCTOR(static void free_args_mem(void)) { +void uv__process_title_cleanup(void) { uv__free(args_mem); /* Keep valgrind happy. */ args_mem = NULL; } diff --git a/Utilities/cmlibuv/src/unix/random-devurandom.c b/Utilities/cmlibuv/src/unix/random-devurandom.c new file mode 100644 index 00000000000..05e52a56a36 --- /dev/null +++ b/Utilities/cmlibuv/src/unix/random-devurandom.c @@ -0,0 +1,93 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +static uv_once_t once = UV_ONCE_INIT; +static int status; + + +int uv__random_readpath(const char* path, void* buf, size_t buflen) { + struct stat s; + size_t pos; + ssize_t n; + int fd; + + fd = uv__open_cloexec(path, O_RDONLY); + + if (fd < 0) + return fd; + + if (fstat(fd, &s)) { + uv__close(fd); + return UV__ERR(errno); + } + + if (!S_ISCHR(s.st_mode)) { + uv__close(fd); + return UV_EIO; + } + + for (pos = 0; pos != buflen; pos += n) { + do + n = read(fd, (char*) buf + pos, buflen - pos); + while (n == -1 && errno == EINTR); + + if (n == -1) { + uv__close(fd); + return UV__ERR(errno); + } + + if (n == 0) { + uv__close(fd); + return UV_EIO; + } + } + + uv__close(fd); + return 0; +} + + +static void uv__random_devurandom_init(void) { + char c; + + /* Linux's random(4) man page suggests applications should read at least + * once from /dev/random before switching to /dev/urandom in order to seed + * the system RNG. Reads from /dev/random can of course block indefinitely + * until entropy is available but that's the point. + */ + status = uv__random_readpath("/dev/random", &c, 1); +} + + +int uv__random_devurandom(void* buf, size_t buflen) { + uv_once(&once, uv__random_devurandom_init); + + if (status != 0) + return status; + + return uv__random_readpath("/dev/urandom", buf, buflen); +} diff --git a/Utilities/cmlibuv/src/unix/random-getentropy.c b/Utilities/cmlibuv/src/unix/random-getentropy.c new file mode 100644 index 00000000000..c45d9fd4a2b --- /dev/null +++ b/Utilities/cmlibuv/src/unix/random-getentropy.c @@ -0,0 +1,57 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +typedef int (*uv__getentropy_cb)(void *, size_t); + +static uv__getentropy_cb uv__getentropy; +static uv_once_t once = UV_ONCE_INIT; + + +static void uv__random_getentropy_init(void) { + uv__getentropy = (uv__getentropy_cb) dlsym(RTLD_DEFAULT, "getentropy"); +} + + +int uv__random_getentropy(void* buf, size_t buflen) { + size_t pos; + size_t stride; + + uv_once(&once, uv__random_getentropy_init); + + if (uv__getentropy == NULL) + return UV_ENOSYS; + + /* getentropy() returns an error for requests > 256 bytes. */ + for (pos = 0, stride = 256; pos + stride < buflen; pos += stride) + if (uv__getentropy((char *) buf + pos, stride)) + return UV__ERR(errno); + + if (uv__getentropy((char *) buf + pos, buflen - pos)) + return UV__ERR(errno); + + return 0; +} diff --git a/Utilities/cmlibuv/src/unix/random-getrandom.c b/Utilities/cmlibuv/src/unix/random-getrandom.c new file mode 100644 index 00000000000..bcc94089bcb --- /dev/null +++ b/Utilities/cmlibuv/src/unix/random-getrandom.c @@ -0,0 +1,88 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#ifdef __linux__ + +#include "linux-syscalls.h" + +#define uv__random_getrandom_init() 0 + +#else /* !__linux__ */ + +#include +#include + +typedef ssize_t (*uv__getrandom_cb)(void *, size_t, unsigned); + +static uv__getrandom_cb uv__getrandom; +static uv_once_t once = UV_ONCE_INIT; + +static void uv__random_getrandom_init_once(void) { + uv__getrandom = (uv__getrandom_cb) dlsym(RTLD_DEFAULT, "getrandom"); +} + +static int uv__random_getrandom_init(void) { + uv_once(&once, uv__random_getrandom_init_once); + + if (uv__getrandom == NULL) + return UV_ENOSYS; + + return 0; +} + +#endif /* !__linux__ */ + +int uv__random_getrandom(void* buf, size_t buflen) { + ssize_t n; + size_t pos; + int rc; + + rc = uv__random_getrandom_init(); + if (rc != 0) + return rc; + + for (pos = 0; pos != buflen; pos += n) { + do { + n = buflen - pos; + + /* Most getrandom() implementations promise that reads <= 256 bytes + * will always succeed and won't be interrupted by signals. + * It's therefore useful to split it up in smaller reads because + * one big read may, in theory, continuously fail with EINTR. + */ + if (n > 256) + n = 256; + + n = uv__getrandom((char *) buf + pos, n, 0); + } while (n == -1 && errno == EINTR); + + if (n == -1) + return UV__ERR(errno); + + if (n == 0) + return UV_EIO; + } + + return 0; +} diff --git a/Utilities/cmlibuv/src/unix/random-sysctl-linux.c b/Utilities/cmlibuv/src/unix/random-sysctl-linux.c new file mode 100644 index 00000000000..66ba8d74ec2 --- /dev/null +++ b/Utilities/cmlibuv/src/unix/random-sysctl-linux.c @@ -0,0 +1,99 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +#include +#include + + +struct uv__sysctl_args { + int* name; + int nlen; + void* oldval; + size_t* oldlenp; + void* newval; + size_t newlen; + unsigned long unused[4]; +}; + + +int uv__random_sysctl(void* buf, size_t buflen) { + static int name[] = {1 /*CTL_KERN*/, 40 /*KERN_RANDOM*/, 6 /*RANDOM_UUID*/}; + struct uv__sysctl_args args; + char uuid[16]; + char* p; + char* pe; + size_t n; + + p = buf; + pe = p + buflen; + + while (p < pe) { + memset(&args, 0, sizeof(args)); + + args.name = name; + args.nlen = ARRAY_SIZE(name); + args.oldval = uuid; + args.oldlenp = &n; + n = sizeof(uuid); + + /* Emits a deprecation warning with some kernels but that seems like + * an okay trade-off for the fallback of the fallback: this function is + * only called when neither getrandom(2) nor /dev/urandom are available. + * Fails with ENOSYS on kernels configured without CONFIG_SYSCTL_SYSCALL. + * At least arm64 never had a _sysctl system call and therefore doesn't + * have a SYS__sysctl define either. + */ +#ifdef SYS__sysctl + if (syscall(SYS__sysctl, &args) == -1) + return UV__ERR(errno); +#else + { + (void) &args; + return UV_ENOSYS; + } +#endif + + if (n != sizeof(uuid)) + return UV_EIO; /* Can't happen. */ + + /* uuid[] is now a type 4 UUID. Bytes 6 and 8 (counting from zero) contain + * 4 and 5 bits of entropy, respectively. For ease of use, we skip those + * and only use 14 of the 16 bytes. + */ + uuid[6] = uuid[14]; + uuid[8] = uuid[15]; + + n = pe - p; + if (n > 14) + n = 14; + + memcpy(p, uuid, n); + p += n; + } + + return 0; +} diff --git a/Utilities/cmlibuv/src/unix/signal.c b/Utilities/cmlibuv/src/unix/signal.c index 01aa55f3fe7..1c83e095bcd 100644 --- a/Utilities/cmlibuv/src/unix/signal.c +++ b/Utilities/cmlibuv/src/unix/signal.c @@ -77,7 +77,7 @@ static void uv__signal_global_init(void) { } -UV_DESTRUCTOR(static void uv__signal_global_fini(void)) { +void uv__signal_cleanup(void) { /* We can only use signal-safe functions here. * That includes read/write and close, fortunately. * We do all of this directly here instead of resetting @@ -98,7 +98,7 @@ UV_DESTRUCTOR(static void uv__signal_global_fini(void)) { static void uv__signal_global_reinit(void) { - uv__signal_global_fini(); + uv__signal_cleanup(); if (uv__make_pipe(uv__signal_lock_pipefd, 0)) abort(); @@ -331,16 +331,7 @@ int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) { void uv__signal_close(uv_signal_t* handle) { - uv__signal_stop(handle); - - /* If there are any caught signals "trapped" in the signal pipe, we can't - * call the close callback yet. Otherwise, add the handle to the finish_close - * queue. - */ - if (handle->caught_signals == handle->dispatched_signals) { - uv__make_close_pending((uv_handle_t*) handle); - } } @@ -375,7 +366,7 @@ static int uv__signal_start(uv_signal_t* handle, /* Short circuit: if the signal watcher is already watching {signum} don't * go through the process of deregistering and registering the handler. - * Additionally, this avoids pending signals getting lost in the small time + * Additionally, this avoids pending signals getting lost in the small * time frame that handle->signum == 0. */ if (signum == handle->signum) { @@ -472,15 +463,6 @@ static void uv__signal_event(uv_loop_t* loop, if (handle->flags & UV_SIGNAL_ONE_SHOT) uv__signal_stop(handle); - - /* If uv_close was called while there were caught signals that were not - * yet dispatched, the uv__finish_close was deferred. Make close pending - * now if this has happened. - */ - if ((handle->flags & UV_HANDLE_CLOSING) && - (handle->caught_signals == handle->dispatched_signals)) { - uv__make_close_pending((uv_handle_t*) handle); - } } bytes -= end; @@ -563,6 +545,7 @@ static void uv__signal_stop(uv_signal_t* handle) { if (first_oneshot && !rem_oneshot) { ret = uv__signal_register_handler(handle->signum, 1); assert(ret == 0); + (void)ret; } } diff --git a/Utilities/cmlibuv/src/unix/stream.c b/Utilities/cmlibuv/src/unix/stream.c index 8121f646444..3b6da8d4649 100644 --- a/Utilities/cmlibuv/src/unix/stream.c +++ b/Utilities/cmlibuv/src/unix/stream.c @@ -1000,12 +1000,12 @@ uv_handle_type uv__handle_type(int fd) { static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) { stream->flags |= UV_HANDLE_READ_EOF; + stream->flags &= ~UV_HANDLE_READING; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); if (!uv__io_active(&stream->io_watcher, POLLOUT)) uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); stream->read_cb(stream, UV_EOF, buf); - stream->flags &= ~UV_HANDLE_READING; } @@ -1048,7 +1048,12 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) { } -#define UV__CMSG_FD_COUNT 64 +#if defined(__PASE__) +/* on IBMi PASE the control message length can not exceed 256. */ +# define UV__CMSG_FD_COUNT 60 +#else +# define UV__CMSG_FD_COUNT 64 +#endif #define UV__CMSG_FD_SIZE (UV__CMSG_FD_COUNT * sizeof(int)) @@ -1403,7 +1408,7 @@ int uv_write2(uv_write_t* req, return UV_EBADF; if (!(stream->flags & UV_HANDLE_WRITABLE)) - return -EPIPE; + return UV_EPIPE; if (send_handle) { if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc) @@ -1557,7 +1562,7 @@ int uv_read_start(uv_stream_t* stream, return UV_EINVAL; if (!(stream->flags & UV_HANDLE_READABLE)) - return -ENOTCONN; + return UV_ENOTCONN; /* The UV_HANDLE_READING flag is irrelevant of the state of the tcp - it just * expresses the desired state of the user. diff --git a/Utilities/cmlibuv/src/unix/sunos.c b/Utilities/cmlibuv/src/unix/sunos.c index 0cd25c10a33..9697648054d 100644 --- a/Utilities/cmlibuv/src/unix/sunos.c +++ b/Utilities/cmlibuv/src/unix/sunos.c @@ -700,16 +700,6 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; - - for (i = 0; i < count; i++) { - uv__free(cpu_infos[i].model); - } - - uv__free(cpu_infos); -} - #ifdef SUNOS_NO_IFADDRS int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { *count = 0; diff --git a/Utilities/cmlibuv/src/unix/tcp.c b/Utilities/cmlibuv/src/unix/tcp.c index 8cedcd6027b..d47e9433dbb 100644 --- a/Utilities/cmlibuv/src/unix/tcp.c +++ b/Utilities/cmlibuv/src/unix/tcp.c @@ -308,6 +308,23 @@ int uv_tcp_getpeername(const uv_tcp_t* handle, } +int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) { + int fd; + struct linger l = { 1, 0 }; + + /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */ + if (handle->flags & UV_HANDLE_SHUTTING) + return UV_EINVAL; + + fd = uv__stream_fd(handle); + if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l))) + return UV__ERR(errno); + + uv_close((uv_handle_t*) handle, close_cb); + return 0; +} + + int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { static int single_accept = -1; unsigned long flags; @@ -362,8 +379,16 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) { return UV__ERR(errno); #ifdef TCP_KEEPIDLE - if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) - return UV__ERR(errno); + if (on) { + int intvl = 1; /* 1 second; same as default on Win32 */ + int cnt = 10; /* 10 retries; same as hardcoded on Win32 */ + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) + return UV__ERR(errno); + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) + return UV__ERR(errno); + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) + return UV__ERR(errno); + } #endif /* Solaris/SmartOS, if you don't support keep-alive, diff --git a/Utilities/cmlibuv/src/unix/thread.c b/Utilities/cmlibuv/src/unix/thread.c index 045322170c7..f93aa532ffb 100644 --- a/Utilities/cmlibuv/src/unix/thread.c +++ b/Utilities/cmlibuv/src/unix/thread.c @@ -37,7 +37,7 @@ #include #endif -#ifdef __GLIBC__ +#if defined(__GLIBC__) && !defined(__UCLIBC__) #include /* gnu_get_libc_version() */ #endif @@ -222,6 +222,12 @@ int uv_thread_create_ex(uv_thread_t* tid, size_t pagesize; size_t stack_size; + /* Used to squelch a -Wcast-function-type warning. */ + union { + void (*in)(void*); + void* (*out)(void*); + } f; + stack_size = params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0; @@ -248,7 +254,8 @@ int uv_thread_create_ex(uv_thread_t* tid, abort(); } - err = pthread_create(tid, attr, (void*(*)(void*)) entry, arg); + f.in = entry; + err = pthread_create(tid, attr, f.out, arg); if (attr != NULL) pthread_attr_destroy(attr); @@ -474,7 +481,7 @@ int uv_sem_trywait(uv_sem_t* sem) { #else /* !(defined(__APPLE__) && defined(__MACH__)) */ -#ifdef __GLIBC__ +#if defined(__GLIBC__) && !defined(__UCLIBC__) /* Hack around https://sourceware.org/bugzilla/show_bug.cgi?id=12674 * by providing a custom implementation for glibc < 2.21 in terms of other @@ -510,7 +517,8 @@ typedef struct uv_semaphore_s { unsigned int value; } uv_semaphore_t; -#if defined(__GLIBC__) || platform_needs_custom_semaphore +#if (defined(__GLIBC__) && !defined(__UCLIBC__)) || \ + platform_needs_custom_semaphore STATIC_ASSERT(sizeof(uv_sem_t) >= sizeof(uv_semaphore_t*)); #endif @@ -639,7 +647,7 @@ static int uv__sem_trywait(uv_sem_t* sem) { } int uv_sem_init(uv_sem_t* sem, unsigned int value) { -#ifdef __GLIBC__ +#if defined(__GLIBC__) && !defined(__UCLIBC__) uv_once(&glibc_version_check_once, glibc_version_check); #endif diff --git a/Utilities/cmlibuv/src/unix/tty.c b/Utilities/cmlibuv/src/unix/tty.c index db479d61bfa..82cd723d741 100644 --- a/Utilities/cmlibuv/src/unix/tty.c +++ b/Utilities/cmlibuv/src/unix/tty.c @@ -34,6 +34,34 @@ #define IMAXBEL 0 #endif +#if defined(__PASE__) +/* On IBM i PASE, for better compatibility with running interactive programs in + * a 5250 environment, isatty() will return true for the stdin/stdout/stderr + * streams created by QSH/QP2TERM. + * + * For more, see docs on PASE_STDIO_ISATTY in + * https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_74/apis/pase_environ.htm + * + * This behavior causes problems for Node as it expects that if isatty() returns + * true that TTY ioctls will be supported by that fd (which is not an + * unreasonable expectation) and when they don't it crashes with assertion + * errors. + * + * Here, we create our own version of isatty() that uses ioctl() to identify + * whether the fd is *really* a TTY or not. + */ +static int isreallyatty(int file) { + int rc; + + rc = !ioctl(file, TXISATTY + 0x81, NULL); + if (!rc && errno != EBADF) + errno = ENOTTY; + + return rc; +} +#define isatty(fd) isreallyatty(fd) +#endif + static int orig_termios_fd = -1; static struct termios orig_termios; static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER; @@ -136,7 +164,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) { * slave device. */ if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0) - r = uv__open_cloexec(path, mode); + r = uv__open_cloexec(path, mode | O_NOCTTY); else r = -1; @@ -362,3 +390,10 @@ int uv_tty_reset_mode(void) { return err; } + +void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) { +} + +int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) { + return UV_ENOTSUP; +} diff --git a/Utilities/cmlibuv/src/unix/udp.c b/Utilities/cmlibuv/src/unix/udp.c index b578e7bc103..21b922fda10 100644 --- a/Utilities/cmlibuv/src/unix/udp.c +++ b/Utilities/cmlibuv/src/unix/udp.c @@ -32,6 +32,8 @@ #endif #include +#define UV__UDP_DGRAM_MAXSIZE (64 * 1024) + #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #endif @@ -49,6 +51,36 @@ static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain, unsigned int flags); +#if HAVE_MMSG + +#define UV__MMSG_MAXWIDTH 20 + +static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf); +static void uv__udp_sendmmsg(uv_udp_t* handle); + +static int uv__recvmmsg_avail; +static int uv__sendmmsg_avail; +static uv_once_t once = UV_ONCE_INIT; + +static void uv__udp_mmsg_init(void) { + int ret; + int s; + s = uv__socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return; + ret = uv__sendmmsg(s, NULL, 0, 0); + if (ret == 0 || errno != ENOSYS) { + uv__sendmmsg_avail = 1; + uv__recvmmsg_avail = 1; + } else { + ret = uv__recvmmsg(s, NULL, 0, 0, NULL); + if (ret == 0 || errno != ENOSYS) + uv__recvmmsg_avail = 1; + } + uv__close(s); +} + +#endif void uv__udp_close(uv_udp_t* handle) { uv__io_close(handle->loop, &handle->io_watcher); @@ -148,6 +180,64 @@ static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) { } } +#if HAVE_MMSG +static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { + struct sockaddr_in6 peers[UV__MMSG_MAXWIDTH]; + struct iovec iov[UV__MMSG_MAXWIDTH]; + struct uv__mmsghdr msgs[UV__MMSG_MAXWIDTH]; + ssize_t nread; + uv_buf_t chunk_buf; + size_t chunks; + int flags; + size_t k; + + /* prepare structures for recvmmsg */ + chunks = buf->len / UV__UDP_DGRAM_MAXSIZE; + if (chunks > ARRAY_SIZE(iov)) + chunks = ARRAY_SIZE(iov); + for (k = 0; k < chunks; ++k) { + iov[k].iov_base = buf->base + k * UV__UDP_DGRAM_MAXSIZE; + iov[k].iov_len = UV__UDP_DGRAM_MAXSIZE; + msgs[k].msg_hdr.msg_iov = iov + k; + msgs[k].msg_hdr.msg_iovlen = 1; + msgs[k].msg_hdr.msg_name = peers + k; + msgs[k].msg_hdr.msg_namelen = sizeof(peers[0]); + msgs[k].msg_hdr.msg_control = NULL; + msgs[k].msg_hdr.msg_controllen = 0; + msgs[k].msg_hdr.msg_flags = 0; + } + + do + nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL); + while (nread == -1 && errno == EINTR); + + if (nread < 1) { + if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK) + handle->recv_cb(handle, 0, buf, NULL, 0); + else + handle->recv_cb(handle, UV__ERR(errno), buf, NULL, 0); + } else { + /* pass each chunk to the application */ + for (k = 0; k < (size_t) nread && handle->recv_cb != NULL; k++) { + flags = UV_UDP_MMSG_CHUNK; + if (msgs[k].msg_hdr.msg_flags & MSG_TRUNC) + flags |= UV_UDP_PARTIAL; + + chunk_buf = uv_buf_init(iov[k].iov_base, iov[k].iov_len); + handle->recv_cb(handle, + msgs[k].msg_len, + &chunk_buf, + msgs[k].msg_hdr.msg_name, + flags); + } + + /* one last callback so the original buffer is freed */ + if (handle->recv_cb != NULL) + handle->recv_cb(handle, 0, buf, NULL, 0); + } + return nread; +} +#endif static void uv__udp_recvmsg(uv_udp_t* handle) { struct sockaddr_storage peer; @@ -165,18 +255,30 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { */ count = 32; - memset(&h, 0, sizeof(h)); - h.msg_name = &peer; - do { buf = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, 64 * 1024, &buf); + handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf); if (buf.base == NULL || buf.len == 0) { handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); return; } assert(buf.base != NULL); +#if HAVE_MMSG + if (handle->flags & UV_HANDLE_UDP_RECVMMSG) { + uv_once(&once, uv__udp_mmsg_init); + if (uv__recvmmsg_avail) { + nread = uv__udp_recvmmsg(handle, &buf); + if (nread > 0) + count -= nread; + continue; + } + } +#endif + + memset(&h, 0, sizeof(h)); + memset(&peer, 0, sizeof(peer)); + h.msg_name = &peer; h.msg_namelen = sizeof(peer); h.msg_iov = (void*) &buf; h.msg_iovlen = 1; @@ -193,33 +295,126 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { handle->recv_cb(handle, UV__ERR(errno), &buf, NULL, 0); } else { - const struct sockaddr *addr; - if (h.msg_namelen == 0) - addr = NULL; - else - addr = (const struct sockaddr*) &peer; - flags = 0; if (h.msg_flags & MSG_TRUNC) flags |= UV_UDP_PARTIAL; - handle->recv_cb(handle, nread, &buf, addr, flags); + handle->recv_cb(handle, nread, &buf, (const struct sockaddr*) &peer, flags); } + count--; } /* recv_cb callback may decide to pause or close the handle */ while (nread != -1 - && count-- > 0 + && count > 0 && handle->io_watcher.fd != -1 && handle->recv_cb != NULL); } +#if HAVE_MMSG +static void uv__udp_sendmmsg(uv_udp_t* handle) { + uv_udp_send_t* req; + struct uv__mmsghdr h[UV__MMSG_MAXWIDTH]; + struct uv__mmsghdr *p; + QUEUE* q; + ssize_t npkts; + size_t pkts; + size_t i; + + if (QUEUE_EMPTY(&handle->write_queue)) + return; + +write_queue_drain: + for (pkts = 0, q = QUEUE_HEAD(&handle->write_queue); + pkts < UV__MMSG_MAXWIDTH && q != &handle->write_queue; + ++pkts, q = QUEUE_HEAD(q)) { + assert(q != NULL); + req = QUEUE_DATA(q, uv_udp_send_t, queue); + assert(req != NULL); + + p = &h[pkts]; + memset(p, 0, sizeof(*p)); + if (req->addr.ss_family == AF_UNSPEC) { + p->msg_hdr.msg_name = NULL; + p->msg_hdr.msg_namelen = 0; + } else { + p->msg_hdr.msg_name = &req->addr; + if (req->addr.ss_family == AF_INET6) + p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in6); + else if (req->addr.ss_family == AF_INET) + p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in); + else if (req->addr.ss_family == AF_UNIX) + p->msg_hdr.msg_namelen = sizeof(struct sockaddr_un); + else { + assert(0 && "unsupported address family"); + abort(); + } + } + h[pkts].msg_hdr.msg_iov = (struct iovec*) req->bufs; + h[pkts].msg_hdr.msg_iovlen = req->nbufs; + } + + do + npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts, 0); + while (npkts == -1 && errno == EINTR); + + if (npkts < 1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + return; + for (i = 0, q = QUEUE_HEAD(&handle->write_queue); + i < pkts && q != &handle->write_queue; + ++i, q = QUEUE_HEAD(q)) { + assert(q != NULL); + req = QUEUE_DATA(q, uv_udp_send_t, queue); + assert(req != NULL); + + req->status = UV__ERR(errno); + QUEUE_REMOVE(&req->queue); + QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + } + uv__io_feed(handle->loop, &handle->io_watcher); + return; + } + + for (i = 0, q = QUEUE_HEAD(&handle->write_queue); + i < pkts && q != &handle->write_queue; + ++i, q = QUEUE_HEAD(&handle->write_queue)) { + assert(q != NULL); + req = QUEUE_DATA(q, uv_udp_send_t, queue); + assert(req != NULL); + + req->status = req->bufs[0].len; + + /* Sending a datagram is an atomic operation: either all data + * is written or nothing is (and EMSGSIZE is raised). That is + * why we don't handle partial writes. Just pop the request + * off the write queue and onto the completed queue, done. + */ + QUEUE_REMOVE(&req->queue); + QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + } + + /* couldn't batch everything, continue sending (jump to avoid stack growth) */ + if (!QUEUE_EMPTY(&handle->write_queue)) + goto write_queue_drain; + uv__io_feed(handle->loop, &handle->io_watcher); + return; +} +#endif static void uv__udp_sendmsg(uv_udp_t* handle) { uv_udp_send_t* req; - QUEUE* q; struct msghdr h; + QUEUE* q; ssize_t size; +#if HAVE_MMSG + uv_once(&once, uv__udp_mmsg_init); + if (uv__sendmmsg_avail) { + uv__udp_sendmmsg(handle); + return; + } +#endif + while (!QUEUE_EMPTY(&handle->write_queue)) { q = QUEUE_HEAD(&handle->write_queue); assert(q != NULL); @@ -269,7 +464,6 @@ static void uv__udp_sendmsg(uv_udp_t* handle) { } } - /* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional * refinements for programs that use multicast. * @@ -659,26 +853,111 @@ static int uv__udp_set_membership6(uv_udp_t* handle, } -int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { - int domain; +#if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__ANDROID__) +static int uv__udp_set_source_membership4(uv_udp_t* handle, + const struct sockaddr_in* multicast_addr, + const char* interface_addr, + const struct sockaddr_in* source_addr, + uv_membership membership) { + struct ip_mreq_source mreq; + int optname; int err; - int fd; - /* Use the lower 8 bits for the domain */ - domain = flags & 0xFF; - if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) - return UV_EINVAL; + err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR); + if (err) + return err; + + memset(&mreq, 0, sizeof(mreq)); + + if (interface_addr != NULL) { + err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr); + if (err) + return err; + } else { + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + } - if (flags & ~0xFF) + mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; + mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr; + + if (membership == UV_JOIN_GROUP) + optname = IP_ADD_SOURCE_MEMBERSHIP; + else if (membership == UV_LEAVE_GROUP) + optname = IP_DROP_SOURCE_MEMBERSHIP; + else return UV_EINVAL; - if (domain != AF_UNSPEC) { - err = uv__socket(domain, SOCK_DGRAM, 0); - if (err < 0) + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IP, + optname, + &mreq, + sizeof(mreq))) { + return UV__ERR(errno); + } + + return 0; +} + + +static int uv__udp_set_source_membership6(uv_udp_t* handle, + const struct sockaddr_in6* multicast_addr, + const char* interface_addr, + const struct sockaddr_in6* source_addr, + uv_membership membership) { + struct group_source_req mreq; + struct sockaddr_in6 addr6; + int optname; + int err; + + err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR); + if (err) + return err; + + memset(&mreq, 0, sizeof(mreq)); + + if (interface_addr != NULL) { + err = uv_ip6_addr(interface_addr, 0, &addr6); + if (err) return err; - fd = err; + mreq.gsr_interface = addr6.sin6_scope_id; } else { - fd = -1; + mreq.gsr_interface = 0; + } + + memcpy(&mreq.gsr_group, multicast_addr, sizeof(mreq.gsr_group)); + memcpy(&mreq.gsr_source, source_addr, sizeof(mreq.gsr_source)); + + if (membership == UV_JOIN_GROUP) + optname = MCAST_JOIN_SOURCE_GROUP; + else if (membership == UV_LEAVE_GROUP) + optname = MCAST_LEAVE_SOURCE_GROUP; + else + return UV_EINVAL; + + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IPV6, + optname, + &mreq, + sizeof(mreq))) { + return UV__ERR(errno); + } + + return 0; +} +#endif + + +int uv__udp_init_ex(uv_loop_t* loop, + uv_udp_t* handle, + unsigned flags, + int domain) { + int fd; + + fd = -1; + if (domain != AF_UNSPEC) { + fd = uv__socket(domain, SOCK_DGRAM, 0); + if (fd < 0) + return fd; } uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP); @@ -694,11 +973,6 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { } -int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { - return uv_udp_init_ex(loop, handle, AF_UNSPEC); -} - - int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { int err; @@ -748,11 +1022,60 @@ int uv_udp_set_membership(uv_udp_t* handle, } } + +int uv_udp_set_source_membership(uv_udp_t* handle, + const char* multicast_addr, + const char* interface_addr, + const char* source_addr, + uv_membership membership) { +#if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__ANDROID__) + int err; + struct sockaddr_storage mcast_addr; + struct sockaddr_in* mcast_addr4; + struct sockaddr_in6* mcast_addr6; + struct sockaddr_storage src_addr; + struct sockaddr_in* src_addr4; + struct sockaddr_in6* src_addr6; + + mcast_addr4 = (struct sockaddr_in*)&mcast_addr; + mcast_addr6 = (struct sockaddr_in6*)&mcast_addr; + src_addr4 = (struct sockaddr_in*)&src_addr; + src_addr6 = (struct sockaddr_in6*)&src_addr; + + err = uv_ip4_addr(multicast_addr, 0, mcast_addr4); + if (err) { + err = uv_ip6_addr(multicast_addr, 0, mcast_addr6); + if (err) + return err; + err = uv_ip6_addr(source_addr, 0, src_addr6); + if (err) + return err; + return uv__udp_set_source_membership6(handle, + mcast_addr6, + interface_addr, + src_addr6, + membership); + } + + err = uv_ip4_addr(source_addr, 0, src_addr4); + if (err) + return err; + return uv__udp_set_source_membership4(handle, + mcast_addr4, + interface_addr, + src_addr4, + membership); +#else + return UV_ENOSYS; +#endif +} + + static int uv__setsockopt(uv_udp_t* handle, int option4, int option6, const void* val, - size_t size) { + socklen_t size) { int r; if (handle->flags & UV_HANDLE_IPV6) @@ -875,7 +1198,7 @@ int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) { * and use the general uv__setsockopt_maybe_char call otherwise. */ #if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \ - defined(__MVS__) + defined(__MVS__) if (handle->flags & UV_HANDLE_IPV6) return uv__setsockopt(handle, IP_MULTICAST_LOOP, diff --git a/Utilities/cmlibuv/src/uv-common.c b/Utilities/cmlibuv/src/uv-common.c index f4853d60fb9..2fcbe3d61aa 100644 --- a/Utilities/cmlibuv/src/uv-common.c +++ b/Utilities/cmlibuv/src/uv-common.c @@ -100,6 +100,17 @@ void* uv__realloc(void* ptr, size_t size) { return NULL; } +void* uv__reallocf(void* ptr, size_t size) { + void* newptr; + + newptr = uv__realloc(ptr, size); + if (newptr == NULL) + if (size > 0) + uv__free(ptr); + + return newptr; +} + int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, @@ -212,6 +223,9 @@ int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) { memset(addr, 0, sizeof(*addr)); addr->sin_family = AF_INET; addr->sin_port = htons(port); +#ifdef SIN6_LEN + addr->sin_len = sizeof(*addr); +#endif return uv_inet_pton(AF_INET, ip, &(addr->sin_addr.s_addr)); } @@ -280,6 +294,36 @@ int uv_tcp_bind(uv_tcp_t* handle, } +int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned flags) { + unsigned extra_flags; + int domain; + int rc; + + /* Use the lower 8 bits for the domain. */ + domain = flags & 0xFF; + if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) + return UV_EINVAL; + + /* Use the higher bits for extra flags. */ + extra_flags = flags & ~0xFF; + if (extra_flags & ~UV_UDP_RECVMMSG) + return UV_EINVAL; + + rc = uv__udp_init_ex(loop, handle, flags, domain); + + if (rc == 0) + if (extra_flags & UV_UDP_RECVMMSG) + handle->flags |= UV_HANDLE_UDP_RECVMMSG; + + return rc; +} + + +int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { + return uv_udp_init_ex(loop, handle, AF_UNSPEC); +} + + int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags) { @@ -788,3 +832,40 @@ void uv_loop_delete(uv_loop_t* loop) { if (loop != default_loop) uv__free(loop); } + + +void uv_os_free_environ(uv_env_item_t* envitems, int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(envitems[i].name); + } + + uv__free(envitems); +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) + uv__free(cpu_infos[i].model); + + uv__free(cpu_infos); +} + + +#ifdef __GNUC__ /* Also covers __clang__ and __INTEL_COMPILER. */ +__attribute__((destructor)) +#endif +void uv_library_shutdown(void) { + static int was_shutdown; + + if (was_shutdown) + return; + + uv__process_title_cleanup(); + uv__signal_cleanup(); + uv__threadpool_cleanup(); + was_shutdown = 1; +} diff --git a/Utilities/cmlibuv/src/uv-common.h b/Utilities/cmlibuv/src/uv-common.h index f788161c395..0b0f5f86a01 100644 --- a/Utilities/cmlibuv/src/uv-common.h +++ b/Utilities/cmlibuv/src/uv-common.h @@ -104,6 +104,7 @@ enum { /* Only used by uv_udp_t handles. */ UV_HANDLE_UDP_PROCESSING = 0x01000000, UV_HANDLE_UDP_CONNECTED = 0x02000000, + UV_HANDLE_UDP_RECVMMSG = 0x04000000, /* Only used by uv_pipe_t handles. */ UV_HANDLE_NON_OVERLAPPED_PIPE = 0x01000000, @@ -138,6 +139,11 @@ int uv__tcp_connect(uv_connect_t* req, unsigned int addrlen, uv_connect_cb cb); +int uv__udp_init_ex(uv_loop_t* loop, + uv_udp_t* handle, + unsigned flags, + int domain); + int uv__udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen, @@ -200,6 +206,10 @@ int uv__next_timeout(const uv_loop_t* loop); void uv__run_timers(uv_loop_t* loop); void uv__timer_close(uv_timer_t* handle); +void uv__process_title_cleanup(void); +void uv__signal_cleanup(void); +void uv__threadpool_cleanup(void); + #define uv__has_active_reqs(loop) \ ((loop)->active_reqs.count > 0) @@ -322,5 +332,6 @@ char *uv__strndup(const char* s, size_t n); void* uv__malloc(size_t size); void uv__free(void* ptr); void* uv__realloc(void* ptr, size_t size); +void* uv__reallocf(void* ptr, size_t size); #endif /* UV_COMMON_H_ */ diff --git a/Utilities/cmlibuv/src/win/core.c b/Utilities/cmlibuv/src/win/core.c index e9d0a581537..9974a115534 100644 --- a/Utilities/cmlibuv/src/win/core.c +++ b/Utilities/cmlibuv/src/win/core.c @@ -321,8 +321,13 @@ void uv__loop_close(uv_loop_t* loop) { uv__loops_remove(loop); - /* close the async handle without needing an extra loop iteration */ - assert(!loop->wq_async.async_sent); + /* Close the async handle without needing an extra loop iteration. + * We might have a pending message, but we're just going to destroy the IOCP + * soon, so we can just discard it now without the usual risk of a getting + * another notification from GetQueuedCompletionStatusEx after calling the + * close_cb (which we also skip defining). We'll assert later that queue was + * actually empty and all reqs handled. */ + loop->wq_async.async_sent = 0; loop->wq_async.close_cb = NULL; uv__handle_closing(&loop->wq_async); uv__handle_close(&loop->wq_async); @@ -444,12 +449,12 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { timeout_time = loop->time + timeout; for (repeat = 0; ; repeat++) { - success = GetQueuedCompletionStatusEx(loop->iocp, - overlappeds, - ARRAY_SIZE(overlappeds), - &count, - timeout, - FALSE); + success = pGetQueuedCompletionStatusEx(loop->iocp, + overlappeds, + ARRAY_SIZE(overlappeds), + &count, + timeout, + FALSE); if (success) { for (i = 0; i < count; i++) { diff --git a/Utilities/cmlibuv/src/win/error.c b/Utilities/cmlibuv/src/win/error.c index 24924ba81ef..3ec984c83eb 100644 --- a/Utilities/cmlibuv/src/win/error.c +++ b/Utilities/cmlibuv/src/win/error.c @@ -72,6 +72,7 @@ int uv_translate_sys_error(int sys_errno) { case ERROR_NOACCESS: return UV_EACCES; case WSAEACCES: return UV_EACCES; case ERROR_ELEVATION_REQUIRED: return UV_EACCES; + case ERROR_CANT_ACCESS_FILE: return UV_EACCES; case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; case WSAEADDRINUSE: return UV_EADDRINUSE; case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; @@ -132,6 +133,7 @@ int uv_translate_sys_error(int sys_errno) { case WSAENOBUFS: return UV_ENOBUFS; case ERROR_BAD_PATHNAME: return UV_ENOENT; case ERROR_DIRECTORY: return UV_ENOENT; + case ERROR_ENVVAR_NOT_FOUND: return UV_ENOENT; case ERROR_FILE_NOT_FOUND: return UV_ENOENT; case ERROR_INVALID_NAME: return UV_ENOENT; case ERROR_INVALID_DRIVE: return UV_ENOENT; diff --git a/Utilities/cmlibuv/src/win/fs-event.c b/Utilities/cmlibuv/src/win/fs-event.c index acf8e1107e9..0126c5ededf 100644 --- a/Utilities/cmlibuv/src/win/fs-event.c +++ b/Utilities/cmlibuv/src/win/fs-event.c @@ -83,6 +83,7 @@ static void uv_relative_path(const WCHAR* filename, static int uv_split_path(const WCHAR* filename, WCHAR** dir, WCHAR** file) { size_t len, i; + DWORD dir_len; if (filename == NULL) { if (dir != NULL) @@ -97,12 +98,16 @@ static int uv_split_path(const WCHAR* filename, WCHAR** dir, if (i == 0) { if (dir) { - *dir = (WCHAR*)uv__malloc((MAX_PATH + 1) * sizeof(WCHAR)); + dir_len = GetCurrentDirectoryW(0, NULL); + if (dir_len == 0) { + return -1; + } + *dir = (WCHAR*)uv__malloc(dir_len * sizeof(WCHAR)); if (!*dir) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } - if (!GetCurrentDirectoryW(MAX_PATH, *dir)) { + if (!GetCurrentDirectoryW(dir_len, *dir)) { uv__free(*dir); *dir = NULL; return -1; @@ -155,9 +160,11 @@ int uv_fs_event_start(uv_fs_event_t* handle, int name_size, is_path_dir, size; DWORD attr, last_error; WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL; - WCHAR short_path_buffer[MAX_PATH]; + DWORD short_path_buffer_len; + WCHAR *short_path_buffer; WCHAR* short_path, *long_path; + short_path = NULL; if (uv__is_active(handle)) return UV_EINVAL; @@ -230,13 +237,23 @@ int uv_fs_event_start(uv_fs_event_t* handle, */ /* Convert to short path. */ + short_path_buffer = NULL; + short_path_buffer_len = GetShortPathNameW(pathw, NULL, 0); + if (short_path_buffer_len == 0) { + goto short_path_done; + } + short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR)); + if (short_path_buffer == NULL) { + goto short_path_done; + } if (GetShortPathNameW(pathw, short_path_buffer, - ARRAY_SIZE(short_path_buffer))) { - short_path = short_path_buffer; - } else { - short_path = NULL; + short_path_buffer_len) == 0) { + uv__free(short_path_buffer); + short_path_buffer = NULL; } +short_path_done: + short_path = short_path_buffer; if (uv_split_path(pathw, &dir, &handle->filew) != 0) { last_error = GetLastError(); @@ -346,6 +363,8 @@ int uv_fs_event_start(uv_fs_event_t* handle, if (uv__is_active(handle)) uv__handle_stop(handle); + uv__free(short_path); + return uv_translate_sys_error(last_error); } diff --git a/Utilities/cmlibuv/src/win/fs-fd-hash-inl.h b/Utilities/cmlibuv/src/win/fs-fd-hash-inl.h new file mode 100644 index 00000000000..7a203d232d3 --- /dev/null +++ b/Utilities/cmlibuv/src/win/fs-fd-hash-inl.h @@ -0,0 +1,178 @@ +#ifndef UV_WIN_FS_FD_HASH_INL_H_ +#define UV_WIN_FS_FD_HASH_INL_H_ + +#include "uv.h" +#include "internal.h" + +/* Files are only inserted in uv__fd_hash when the UV_FS_O_FILEMAP flag is + * specified. Thus, when uv__fd_hash_get returns true, the file mapping in the + * info structure should be used for read/write operations. + * + * If the file is empty, the mapping field will be set to + * INVALID_HANDLE_VALUE. This is not an issue since the file mapping needs to + * be created anyway when the file size changes. + * + * Since file descriptors are sequential integers, the modulo operator is used + * as hashing function. For each bucket, a single linked list of arrays is + * kept to minimize allocations. A statically allocated memory buffer is kept + * for the first array in each bucket. */ + + +#define UV__FD_HASH_SIZE 256 +#define UV__FD_HASH_GROUP_SIZE 16 + +struct uv__fd_info_s { + int flags; + BOOLEAN is_directory; + HANDLE mapping; + LARGE_INTEGER size; + LARGE_INTEGER current_pos; +}; + +struct uv__fd_hash_entry_s { + uv_file fd; + struct uv__fd_info_s info; +}; + +struct uv__fd_hash_entry_group_s { + struct uv__fd_hash_entry_s entries[UV__FD_HASH_GROUP_SIZE]; + struct uv__fd_hash_entry_group_s* next; +}; + +struct uv__fd_hash_bucket_s { + size_t size; + struct uv__fd_hash_entry_group_s* data; +}; + + +static uv_mutex_t uv__fd_hash_mutex; + +static struct uv__fd_hash_entry_group_s + uv__fd_hash_entry_initial[UV__FD_HASH_SIZE * UV__FD_HASH_GROUP_SIZE]; +static struct uv__fd_hash_bucket_s uv__fd_hash[UV__FD_HASH_SIZE]; + + +INLINE static void uv__fd_hash_init(void) { + int i, err; + + err = uv_mutex_init(&uv__fd_hash_mutex); + if (err) { + uv_fatal_error(err, "uv_mutex_init"); + } + + for (i = 0; i < ARRAY_SIZE(uv__fd_hash); ++i) { + uv__fd_hash[i].size = 0; + uv__fd_hash[i].data = + uv__fd_hash_entry_initial + i * UV__FD_HASH_GROUP_SIZE; + } +} + +#define FIND_COMMON_VARIABLES \ + unsigned i; \ + unsigned bucket = fd % ARRAY_SIZE(uv__fd_hash); \ + struct uv__fd_hash_entry_s* entry_ptr = NULL; \ + struct uv__fd_hash_entry_group_s* group_ptr; \ + struct uv__fd_hash_bucket_s* bucket_ptr = &uv__fd_hash[bucket]; + +#define FIND_IN_GROUP_PTR(group_size) \ + do { \ + for (i = 0; i < group_size; ++i) { \ + if (group_ptr->entries[i].fd == fd) { \ + entry_ptr = &group_ptr->entries[i]; \ + break; \ + } \ + } \ + } while (0) + +#define FIND_IN_BUCKET_PTR() \ + do { \ + size_t first_group_size = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE; \ + if (bucket_ptr->size != 0 && first_group_size == 0) \ + first_group_size = UV__FD_HASH_GROUP_SIZE; \ + group_ptr = bucket_ptr->data; \ + FIND_IN_GROUP_PTR(first_group_size); \ + for (group_ptr = group_ptr->next; \ + group_ptr != NULL && entry_ptr == NULL; \ + group_ptr = group_ptr->next) \ + FIND_IN_GROUP_PTR(UV__FD_HASH_GROUP_SIZE); \ + } while (0) + +INLINE static int uv__fd_hash_get(int fd, struct uv__fd_info_s* info) { + FIND_COMMON_VARIABLES + + uv_mutex_lock(&uv__fd_hash_mutex); + + FIND_IN_BUCKET_PTR(); + + if (entry_ptr != NULL) { + *info = entry_ptr->info; + } + + uv_mutex_unlock(&uv__fd_hash_mutex); + return entry_ptr != NULL; +} + +INLINE static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) { + FIND_COMMON_VARIABLES + + uv_mutex_lock(&uv__fd_hash_mutex); + + FIND_IN_BUCKET_PTR(); + + if (entry_ptr == NULL) { + i = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE; + + if (bucket_ptr->size != 0 && i == 0) { + struct uv__fd_hash_entry_group_s* new_group_ptr = + uv__malloc(sizeof(*new_group_ptr)); + if (new_group_ptr == NULL) { + uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); + } + new_group_ptr->next = bucket_ptr->data; + bucket_ptr->data = new_group_ptr; + } + + bucket_ptr->size += 1; + entry_ptr = &bucket_ptr->data->entries[i]; + entry_ptr->fd = fd; + } + + entry_ptr->info = *info; + + uv_mutex_unlock(&uv__fd_hash_mutex); +} + +INLINE static int uv__fd_hash_remove(int fd, struct uv__fd_info_s* info) { + FIND_COMMON_VARIABLES + + uv_mutex_lock(&uv__fd_hash_mutex); + + FIND_IN_BUCKET_PTR(); + + if (entry_ptr != NULL) { + *info = entry_ptr->info; + + bucket_ptr->size -= 1; + + i = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE; + if (entry_ptr != &bucket_ptr->data->entries[i]) { + *entry_ptr = bucket_ptr->data->entries[i]; + } + + if (bucket_ptr->size != 0 && + bucket_ptr->size % UV__FD_HASH_GROUP_SIZE == 0) { + struct uv__fd_hash_entry_group_s* old_group_ptr = bucket_ptr->data; + bucket_ptr->data = old_group_ptr->next; + uv__free(old_group_ptr); + } + } + + uv_mutex_unlock(&uv__fd_hash_mutex); + return entry_ptr != NULL; +} + +#undef FIND_COMMON_VARIABLES +#undef FIND_IN_GROUP_PTR +#undef FIND_IN_BUCKET_PTR + +#endif /* UV_WIN_FS_FD_HASH_INL_H_ */ diff --git a/Utilities/cmlibuv/src/win/fs.c b/Utilities/cmlibuv/src/win/fs.c index 9e2f084c8d0..9577bc02d3c 100644 --- a/Utilities/cmlibuv/src/win/fs.c +++ b/Utilities/cmlibuv/src/win/fs.c @@ -34,8 +34,7 @@ #include "internal.h" #include "req-inl.h" #include "handle-inl.h" - -#include +#include "fs-fd-hash-inl.h" #define UV_FS_FREE_PATHS 0x0002 @@ -126,6 +125,8 @@ #define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \ ((c) >= L'A' && (c) <= L'Z')) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + const WCHAR JUNCTION_PREFIX[] = L"\\??\\"; const WCHAR JUNCTION_PREFIX_LEN = 4; @@ -137,8 +138,16 @@ const WCHAR UNC_PATH_PREFIX_LEN = 8; static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; +static DWORD uv__allocation_granularity; + + void uv_fs_init(void) { - _fmode = _O_BINARY; + SYSTEM_INFO system_info; + + GetSystemInfo(&system_info); + uv__allocation_granularity = system_info.dwAllocationGranularity; + + uv__fd_hash_init(); } @@ -248,6 +257,7 @@ INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, req->loop = loop; req->flags = 0; req->fs_type = fs_type; + req->sys_errno_ = 0; req->result = 0; req->ptr = NULL; req->path = NULL; @@ -312,6 +322,8 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, WCHAR* w_target; DWORD w_target_len; DWORD bytes; + size_t i; + size_t len; if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, @@ -396,6 +408,38 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, w_target += 4; w_target_len -= 4; + } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) { + /* String #3 in the list has the target filename. */ + if (reparse_data->AppExecLinkReparseBuffer.StringCount < 3) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + w_target = reparse_data->AppExecLinkReparseBuffer.StringList; + /* The StringList buffer contains a list of strings separated by "\0", */ + /* with "\0\0" terminating the list. Move to the 3rd string in the list: */ + for (i = 0; i < 2; ++i) { + len = wcslen(w_target); + if (len == 0) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + w_target += len + 1; + } + w_target_len = wcslen(w_target); + if (w_target_len == 0) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + /* Make sure it is an absolute path. */ + if (!(w_target_len >= 3 && + ((w_target[0] >= L'a' && w_target[0] <= L'z') || + (w_target[0] >= L'A' && w_target[0] <= L'Z')) && + w_target[1] == L':' && + w_target[2] == L'\\')) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + } else { /* Reparse tag does not indicate a symlink. */ SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); @@ -414,6 +458,27 @@ void fs__open(uv_fs_t* req) { HANDLE file; int fd, current_umask; int flags = req->fs.info.file_flags; + struct uv__fd_info_s fd_info; + + /* Adjust flags to be compatible with the memory file mapping. Save the + * original flags to emulate the correct behavior. */ + if (flags & UV_FS_O_FILEMAP) { + fd_info.flags = flags; + fd_info.current_pos.QuadPart = 0; + + if ((flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) == + UV_FS_O_WRONLY) { + /* CreateFileMapping always needs read access */ + flags = (flags & ~UV_FS_O_WRONLY) | UV_FS_O_RDWR; + } + + if (flags & UV_FS_O_APPEND) { + /* Clear the append flag and ensure RDRW mode */ + flags &= ~UV_FS_O_APPEND; + flags &= ~(UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + flags |= UV_FS_O_RDWR; + } + } /* Obtain the active umask. umask() never fails and returns the previous * umask. */ @@ -444,7 +509,8 @@ void fs__open(uv_fs_t* req) { * Here is where we deviate significantly from what CRT's _open() * does. We indiscriminately use all the sharing modes, to match * UNIX semantics. In particular, this ensures that the file can - * be deleted even whilst it's open, fixing issue #1449. + * be deleted even whilst it's open, fixing issue + * https://github.com/nodejs/node-v0.x-archive/issues/1449. * We still support exclusive sharing mode, since it is necessary * for opening raw block devices, otherwise Windows will prevent * any attempt to write past the master boot record. @@ -583,11 +649,55 @@ void fs__open(uv_fs_t* req) { else if (GetLastError() != ERROR_SUCCESS) SET_REQ_WIN32_ERROR(req, GetLastError()); else - SET_REQ_WIN32_ERROR(req, UV_UNKNOWN); + SET_REQ_WIN32_ERROR(req, (DWORD) UV_UNKNOWN); CloseHandle(file); return; } + if (flags & UV_FS_O_FILEMAP) { + FILE_STANDARD_INFO file_info; + if (!GetFileInformationByHandleEx(file, + FileStandardInfo, + &file_info, + sizeof file_info)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + return; + } + fd_info.is_directory = file_info.Directory; + + if (fd_info.is_directory) { + fd_info.size.QuadPart = 0; + fd_info.mapping = INVALID_HANDLE_VALUE; + } else { + if (!GetFileSizeEx(file, &fd_info.size)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + return; + } + + if (fd_info.size.QuadPart == 0) { + fd_info.mapping = INVALID_HANDLE_VALUE; + } else { + DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | + UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE; + fd_info.mapping = CreateFileMapping(file, + NULL, + flProtect, + fd_info.size.HighPart, + fd_info.size.LowPart, + NULL); + if (fd_info.mapping == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + return; + } + } + } + + uv__fd_hash_add(fd, &fd_info); + } + SET_REQ_RESULT(req, fd); return; @@ -598,9 +708,16 @@ void fs__open(uv_fs_t* req) { void fs__close(uv_fs_t* req) { int fd = req->file.fd; int result; + struct uv__fd_info_s fd_info; VERIFY_FD(fd, req); + if (uv__fd_hash_remove(fd, &fd_info)) { + if (fd_info.mapping != INVALID_HANDLE_VALUE) { + CloseHandle(fd_info.mapping); + } + } + if (fd > 2) result = _close(fd); else @@ -618,6 +735,123 @@ void fs__close(uv_fs_t* req) { } +LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep, + int* perror) { + if (excode != EXCEPTION_IN_PAGE_ERROR) { + return EXCEPTION_CONTINUE_SEARCH; + } + + assert(perror != NULL); + if (pep != NULL && pep->ExceptionRecord != NULL && + pep->ExceptionRecord->NumberParameters >= 3) { + NTSTATUS status = (NTSTATUS)pep->ExceptionRecord->ExceptionInformation[3]; + *perror = pRtlNtStatusToDosError(status); + if (*perror != ERROR_SUCCESS) { + return EXCEPTION_EXECUTE_HANDLER; + } + } + *perror = UV_UNKNOWN; + return EXCEPTION_EXECUTE_HANDLER; +} + + +void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) { + int fd = req->file.fd; /* VERIFY_FD done in fs__read */ + int rw_flags = fd_info->flags & + (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + size_t read_size, done_read; + unsigned int index; + LARGE_INTEGER pos, end_pos; + size_t view_offset; + LARGE_INTEGER view_base; + void* view; + + if (rw_flags == UV_FS_O_WRONLY) { + SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + return; + } + if (fd_info->is_directory) { + SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION); + return; + } + + if (req->fs.info.offset == -1) { + pos = fd_info->current_pos; + } else { + pos.QuadPart = req->fs.info.offset; + } + + /* Make sure we wont read past EOF. */ + if (pos.QuadPart >= fd_info->size.QuadPart) { + SET_REQ_RESULT(req, 0); + return; + } + + read_size = 0; + for (index = 0; index < req->fs.info.nbufs; ++index) { + read_size += req->fs.info.bufs[index].len; + } + read_size = (size_t) MIN((LONGLONG) read_size, + fd_info->size.QuadPart - pos.QuadPart); + if (read_size == 0) { + SET_REQ_RESULT(req, 0); + return; + } + + end_pos.QuadPart = pos.QuadPart + read_size; + + view_offset = pos.QuadPart % uv__allocation_granularity; + view_base.QuadPart = pos.QuadPart - view_offset; + view = MapViewOfFile(fd_info->mapping, + FILE_MAP_READ, + view_base.HighPart, + view_base.LowPart, + view_offset + read_size); + if (view == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + done_read = 0; + for (index = 0; + index < req->fs.info.nbufs && done_read < read_size; + ++index) { + int err = 0; + size_t this_read_size = MIN(req->fs.info.bufs[index].len, + read_size - done_read); +#ifdef _MSC_VER + __try { +#endif + memcpy(req->fs.info.bufs[index].base, + (char*)view + view_offset + done_read, + this_read_size); +#ifdef _MSC_VER + } + __except (fs__filemap_ex_filter(GetExceptionCode(), + GetExceptionInformation(), &err)) { + SET_REQ_WIN32_ERROR(req, err); + UnmapViewOfFile(view); + return; + } +#endif + done_read += this_read_size; + } + assert(done_read == read_size); + + if (!UnmapViewOfFile(view)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + if (req->fs.info.offset == -1) { + fd_info->current_pos = end_pos; + uv__fd_hash_add(fd, fd_info); + } + + SET_REQ_RESULT(req, read_size); + return; +} + void fs__read(uv_fs_t* req) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; @@ -631,9 +865,15 @@ void fs__read(uv_fs_t* req) { LARGE_INTEGER original_position; LARGE_INTEGER zero_offset; int restore_position; + struct uv__fd_info_s fd_info; VERIFY_FD(fd, req); + if (uv__fd_hash_get(fd, &fd_info)) { + fs__read_filemap(req, &fd_info); + return; + } + zero_offset.QuadPart = 0; restore_position = 0; handle = uv__get_osfhandle(fd); @@ -690,6 +930,131 @@ void fs__read(uv_fs_t* req) { } +void fs__write_filemap(uv_fs_t* req, HANDLE file, + struct uv__fd_info_s* fd_info) { + int fd = req->file.fd; /* VERIFY_FD done in fs__write */ + int force_append = fd_info->flags & UV_FS_O_APPEND; + int rw_flags = fd_info->flags & + (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + size_t write_size, done_write; + unsigned int index; + LARGE_INTEGER zero, pos, end_pos; + size_t view_offset; + LARGE_INTEGER view_base; + void* view; + FILETIME ft; + + if (rw_flags == UV_FS_O_RDONLY) { + SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + return; + } + if (fd_info->is_directory) { + SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION); + return; + } + + write_size = 0; + for (index = 0; index < req->fs.info.nbufs; ++index) { + write_size += req->fs.info.bufs[index].len; + } + + if (write_size == 0) { + SET_REQ_RESULT(req, 0); + return; + } + + zero.QuadPart = 0; + if (force_append) { + pos = fd_info->size; + } else if (req->fs.info.offset == -1) { + pos = fd_info->current_pos; + } else { + pos.QuadPart = req->fs.info.offset; + } + + end_pos.QuadPart = pos.QuadPart + write_size; + + /* Recreate the mapping to enlarge the file if needed */ + if (end_pos.QuadPart > fd_info->size.QuadPart) { + if (fd_info->mapping != INVALID_HANDLE_VALUE) { + CloseHandle(fd_info->mapping); + } + + fd_info->mapping = CreateFileMapping(file, + NULL, + PAGE_READWRITE, + end_pos.HighPart, + end_pos.LowPart, + NULL); + if (fd_info->mapping == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + fd_info->mapping = INVALID_HANDLE_VALUE; + fd_info->size.QuadPart = 0; + fd_info->current_pos.QuadPart = 0; + uv__fd_hash_add(fd, fd_info); + return; + } + + fd_info->size = end_pos; + uv__fd_hash_add(fd, fd_info); + } + + view_offset = pos.QuadPart % uv__allocation_granularity; + view_base.QuadPart = pos.QuadPart - view_offset; + view = MapViewOfFile(fd_info->mapping, + FILE_MAP_WRITE, + view_base.HighPart, + view_base.LowPart, + view_offset + write_size); + if (view == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + done_write = 0; + for (index = 0; index < req->fs.info.nbufs; ++index) { + int err = 0; +#ifdef _MSC_VER + __try { +#endif + memcpy((char*)view + view_offset + done_write, + req->fs.info.bufs[index].base, + req->fs.info.bufs[index].len); +#ifdef _MSC_VER + } + __except (fs__filemap_ex_filter(GetExceptionCode(), + GetExceptionInformation(), &err)) { + SET_REQ_WIN32_ERROR(req, err); + UnmapViewOfFile(view); + return; + } +#endif + done_write += req->fs.info.bufs[index].len; + } + assert(done_write == write_size); + + if (!FlushViewOfFile(view, 0)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + UnmapViewOfFile(view); + return; + } + if (!UnmapViewOfFile(view)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + if (req->fs.info.offset == -1) { + fd_info->current_pos = end_pos; + uv__fd_hash_add(fd, fd_info); + } + + GetSystemTimeAsFileTime(&ft); + SetFileTime(file, NULL, NULL, &ft); + + SET_REQ_RESULT(req, done_write); +} + void fs__write(uv_fs_t* req) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; @@ -702,6 +1067,7 @@ void fs__write(uv_fs_t* req) { LARGE_INTEGER original_position; LARGE_INTEGER zero_offset; int restore_position; + struct uv__fd_info_s fd_info; VERIFY_FD(fd, req); @@ -713,6 +1079,11 @@ void fs__write(uv_fs_t* req) { return; } + if (uv__fd_hash_get(fd, &fd_info)) { + fs__write_filemap(req, handle, &fd_info); + return; + } + if (offset != -1) { memset(&overlapped, 0, sizeof overlapped); overlapped_ptr = &overlapped; @@ -850,13 +1221,19 @@ void fs__unlink(uv_fs_t* req) { void fs__mkdir(uv_fs_t* req) { /* TODO: use req->mode. */ - int result = _wmkdir(req->file.pathw); - SET_REQ_RESULT(req, result); + req->result = _wmkdir(req->file.pathw); + if (req->result == -1) { + req->sys_errno_ = _doserrno; + req->result = req->sys_errno_ == ERROR_INVALID_NAME + ? UV_EINVAL + : uv_translate_sys_error(req->sys_errno_); + } } +typedef int (*uv__fs_mktemp_func)(uv_fs_t* req); /* OpenBSD original: lib/libc/stdio/mktemp.c */ -void fs__mkdtemp(uv_fs_t* req) { +void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) { static const WCHAR *tempchars = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static const size_t num_chars = 62; @@ -864,9 +1241,7 @@ void fs__mkdtemp(uv_fs_t* req) { WCHAR *cp, *ep; unsigned int tries, i; size_t len; - HCRYPTPROV h_crypt_prov; uint64_t v; - BOOL released; len = wcslen(req->file.pathw); ep = req->file.pathw + len; @@ -875,16 +1250,10 @@ void fs__mkdtemp(uv_fs_t* req) { return; } - if (!CryptAcquireContext(&h_crypt_prov, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - return; - } - tries = TMP_MAX; do { - if (!CryptGenRandom(h_crypt_prov, sizeof(v), (BYTE*) &v)) { - SET_REQ_WIN32_ERROR(req, GetLastError()); + if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) { + SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE); break; } @@ -894,25 +1263,92 @@ void fs__mkdtemp(uv_fs_t* req) { v /= num_chars; } - if (_wmkdir(req->file.pathw) == 0) { - len = strlen(req->path); - wcstombs((char*) req->path + len - num_x, ep - num_x, num_x); - SET_REQ_RESULT(req, 0); - break; - } else if (errno != EEXIST) { - SET_REQ_RESULT(req, -1); + if (func(req)) { + if (req->result >= 0) { + len = strlen(req->path); + wcstombs((char*) req->path + len - num_x, ep - num_x, num_x); + } break; } } while (--tries); - released = CryptReleaseContext(h_crypt_prov, 0); - assert(released); if (tries == 0) { SET_REQ_RESULT(req, -1); } } +static int fs__mkdtemp_func(uv_fs_t* req) { + if (_wmkdir(req->file.pathw) == 0) { + SET_REQ_RESULT(req, 0); + return 1; + } else if (errno != EEXIST) { + SET_REQ_RESULT(req, -1); + return 1; + } + + return 0; +} + + +void fs__mkdtemp(uv_fs_t* req) { + fs__mktemp(req, fs__mkdtemp_func); +} + + +static int fs__mkstemp_func(uv_fs_t* req) { + HANDLE file; + int fd; + + file = CreateFileW(req->file.pathw, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (file == INVALID_HANDLE_VALUE) { + DWORD error; + error = GetLastError(); + + /* If the file exists, the main fs__mktemp() function + will retry. If it's another error, we want to stop. */ + if (error != ERROR_FILE_EXISTS) { + SET_REQ_WIN32_ERROR(req, error); + return 1; + } + + return 0; + } + + fd = _open_osfhandle((intptr_t) file, 0); + if (fd < 0) { + /* The only known failure mode for _open_osfhandle() is EMFILE, in which + * case GetLastError() will return zero. However we'll try to handle other + * errors as well, should they ever occur. + */ + if (errno == EMFILE) + SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES); + else if (GetLastError() != ERROR_SUCCESS) + SET_REQ_WIN32_ERROR(req, GetLastError()); + else + SET_REQ_WIN32_ERROR(req, UV_UNKNOWN); + CloseHandle(file); + return 1; + } + + SET_REQ_RESULT(req, fd); + + return 1; +} + + +void fs__mkstemp(uv_fs_t* req) { + fs__mktemp(req, fs__mkstemp_func); +} + + void fs__scandir(uv_fs_t* req) { static const size_t dirents_initial_size = 32; @@ -1409,47 +1845,57 @@ INLINE static void fs__stat_prepare_path(WCHAR* pathw) { } -INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { +INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, + int do_lstat, + uv_stat_t* statbuf) { HANDLE handle; DWORD flags; + DWORD ret; flags = FILE_FLAG_BACKUP_SEMANTICS; - if (do_lstat) { + if (do_lstat) flags |= FILE_FLAG_OPEN_REPARSE_POINT; - } - handle = CreateFileW(req->file.pathw, + handle = CreateFileW(path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags, NULL); - if (handle == INVALID_HANDLE_VALUE) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - return; - } - if (fs__stat_handle(handle, &req->statbuf, do_lstat) != 0) { - DWORD error = GetLastError(); + if (handle == INVALID_HANDLE_VALUE) + ret = GetLastError(); + else if (fs__stat_handle(handle, statbuf, do_lstat) != 0) + ret = GetLastError(); + else + ret = 0; + + CloseHandle(handle); + return ret; +} + + +INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { + DWORD error; + + error = fs__stat_impl_from_path(req->file.pathw, do_lstat, &req->statbuf); + if (error != 0) { if (do_lstat && (error == ERROR_SYMLINK_NOT_SUPPORTED || error == ERROR_NOT_A_REPARSE_POINT)) { /* We opened a reparse point but it was not a symlink. Try again. */ fs__stat_impl(req, 0); - } else { /* Stat failed. */ - SET_REQ_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, error); } - CloseHandle(handle); return; } req->ptr = &req->statbuf; req->result = 0; - CloseHandle(handle); } @@ -1526,6 +1972,7 @@ static void fs__fdatasync(uv_fs_t* req) { static void fs__ftruncate(uv_fs_t* req) { int fd = req->file.fd; HANDLE handle; + struct uv__fd_info_s fd_info = { 0 }; NTSTATUS status; IO_STATUS_BLOCK io_status; FILE_END_OF_FILE_INFORMATION eof_info; @@ -1534,6 +1981,17 @@ static void fs__ftruncate(uv_fs_t* req) { handle = uv__get_osfhandle(fd); + if (uv__fd_hash_get(fd, &fd_info)) { + if (fd_info.is_directory) { + SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + return; + } + + if (fd_info.mapping != INVALID_HANDLE_VALUE) { + CloseHandle(fd_info.mapping); + } + } + eof_info.EndOfFile.QuadPart = req->fs.info.offset; status = pNtSetInformationFile(handle, @@ -1546,6 +2004,43 @@ static void fs__ftruncate(uv_fs_t* req) { SET_REQ_RESULT(req, 0); } else { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); + + if (fd_info.flags) { + CloseHandle(handle); + fd_info.mapping = INVALID_HANDLE_VALUE; + fd_info.size.QuadPart = 0; + fd_info.current_pos.QuadPart = 0; + uv__fd_hash_add(fd, &fd_info); + return; + } + } + + if (fd_info.flags) { + fd_info.size = eof_info.EndOfFile; + + if (fd_info.size.QuadPart == 0) { + fd_info.mapping = INVALID_HANDLE_VALUE; + } else { + DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | + UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE; + fd_info.mapping = CreateFileMapping(handle, + NULL, + flProtect, + fd_info.size.HighPart, + fd_info.size.LowPart, + NULL); + if (fd_info.mapping == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(handle); + fd_info.mapping = INVALID_HANDLE_VALUE; + fd_info.size.QuadPart = 0; + fd_info.current_pos.QuadPart = 0; + uv__fd_hash_add(fd, &fd_info); + return; + } + } + + uv__fd_hash_add(fd, &fd_info); } } @@ -1553,6 +2048,8 @@ static void fs__ftruncate(uv_fs_t* req) { static void fs__copyfile(uv_fs_t* req) { int flags; int overwrite; + uv_stat_t statbuf; + uv_stat_t new_statbuf; flags = req->fs.info.file_flags; @@ -1563,12 +2060,25 @@ static void fs__copyfile(uv_fs_t* req) { overwrite = flags & UV_FS_COPYFILE_EXCL; - if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) == 0) { - SET_REQ_WIN32_ERROR(req, GetLastError()); + if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) != 0) { + SET_REQ_RESULT(req, 0); return; } - SET_REQ_RESULT(req, 0); + SET_REQ_WIN32_ERROR(req, GetLastError()); + if (req->result != UV_EBUSY) + return; + + /* if error UV_EBUSY check if src and dst file are the same */ + if (fs__stat_impl_from_path(req->file.pathw, 0, &statbuf) != 0 || + fs__stat_impl_from_path(req->fs.info.new_pathw, 0, &new_statbuf) != 0) { + return; + } + + if (statbuf.st_dev == new_statbuf.st_dev && + statbuf.st_ino == new_statbuf.st_ino) { + SET_REQ_RESULT(req, 0); + } } @@ -1750,34 +2260,68 @@ INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) { return 0; } - -static void fs__utime(uv_fs_t* req) { +INLINE static DWORD fs__utime_impl_from_path(WCHAR* path, + double atime, + double mtime, + int do_lutime) { HANDLE handle; + DWORD flags; + DWORD ret; - handle = CreateFileW(req->file.pathw, + flags = FILE_FLAG_BACKUP_SEMANTICS; + if (do_lutime) { + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + + handle = CreateFileW(path, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, + flags, NULL); if (handle == INVALID_HANDLE_VALUE) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - return; + ret = GetLastError(); + } else if (fs__utime_handle(handle, atime, mtime) != 0) { + ret = GetLastError(); + } else { + ret = 0; } - if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - CloseHandle(handle); + CloseHandle(handle); + return ret; +} + +INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) { + DWORD error; + + error = fs__utime_impl_from_path(req->file.pathw, + req->fs.time.atime, + req->fs.time.mtime, + do_lutime); + + if (error != 0) { + if (do_lutime && + (error == ERROR_SYMLINK_NOT_SUPPORTED || + error == ERROR_NOT_A_REPARSE_POINT)) { + /* Opened file is a reparse point but not a symlink. Try again. */ + fs__utime_impl(req, 0); + } else { + /* utime failed. */ + SET_REQ_WIN32_ERROR(req, error); + } + return; } - CloseHandle(handle); - req->result = 0; } +static void fs__utime(uv_fs_t* req) { + fs__utime_impl(req, /* do_lutime */ 0); +} + static void fs__futime(uv_fs_t* req) { int fd = req->file.fd; @@ -1799,6 +2343,10 @@ static void fs__futime(uv_fs_t* req) { req->result = 0; } +static void fs__lutime(uv_fs_t* req) { + fs__utime_impl(req, /* do_lutime */ 1); +} + static void fs__link(uv_fs_t* req) { DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL); @@ -2139,6 +2687,90 @@ static void fs__lchown(uv_fs_t* req) { req->result = 0; } + +static void fs__statfs(uv_fs_t* req) { + uv_statfs_t* stat_fs; + DWORD sectors_per_cluster; + DWORD bytes_per_sector; + DWORD free_clusters; + DWORD total_clusters; + WCHAR* pathw; + + pathw = req->file.pathw; +retry_get_disk_free_space: + if (0 == GetDiskFreeSpaceW(pathw, + §ors_per_cluster, + &bytes_per_sector, + &free_clusters, + &total_clusters)) { + DWORD err; + WCHAR* fpart; + size_t len; + DWORD ret; + BOOL is_second; + + err = GetLastError(); + is_second = pathw != req->file.pathw; + if (err != ERROR_DIRECTORY || is_second) { + if (is_second) + uv__free(pathw); + + SET_REQ_WIN32_ERROR(req, err); + return; + } + + len = MAX_PATH + 1; + pathw = uv__malloc(len * sizeof(*pathw)); + if (pathw == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + return; + } +retry_get_full_path_name: + ret = GetFullPathNameW(req->file.pathw, + len, + pathw, + &fpart); + if (ret == 0) { + uv__free(pathw); + SET_REQ_WIN32_ERROR(req, err); + return; + } else if (ret > len) { + len = ret; + pathw = uv__reallocf(pathw, len * sizeof(*pathw)); + if (pathw == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + return; + } + goto retry_get_full_path_name; + } + if (fpart != 0) + *fpart = L'\0'; + + goto retry_get_disk_free_space; + } + if (pathw != req->file.pathw) { + uv__free(pathw); + } + + stat_fs = uv__malloc(sizeof(*stat_fs)); + if (stat_fs == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + return; + } + + stat_fs->f_type = 0; + stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster; + stat_fs->f_blocks = total_clusters; + stat_fs->f_bfree = free_clusters; + stat_fs->f_bavail = free_clusters; + stat_fs->f_files = 0; + stat_fs->f_ffree = 0; + req->ptr = stat_fs; + req->flags |= UV_FS_FREE_PTR; + SET_REQ_RESULT(req, 0); +} + + static void uv__fs_work(struct uv__work* w) { uv_fs_t* req; @@ -2159,6 +2791,7 @@ static void uv__fs_work(struct uv__work* w) { XX(FTRUNCATE, ftruncate) XX(UTIME, utime) XX(FUTIME, futime) + XX(LUTIME, lutime) XX(ACCESS, access) XX(CHMOD, chmod) XX(FCHMOD, fchmod) @@ -2168,6 +2801,7 @@ static void uv__fs_work(struct uv__work* w) { XX(RMDIR, rmdir) XX(MKDIR, mkdir) XX(MKDTEMP, mkdtemp) + XX(MKSTEMP, mkstemp) XX(RENAME, rename) XX(SCANDIR, scandir) XX(READDIR, readdir) @@ -2178,8 +2812,9 @@ static void uv__fs_work(struct uv__work* w) { XX(READLINK, readlink) XX(REALPATH, realpath) XX(CHOWN, chown) - XX(FCHOWN, fchown); - XX(LCHOWN, lchown); + XX(FCHOWN, fchown) + XX(LCHOWN, lchown) + XX(STATFS, statfs) default: assert(!"bad uv_fs_type"); } @@ -2240,7 +2875,8 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, INIT(UV_FS_OPEN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.file_flags = flags; @@ -2265,8 +2901,10 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_cb cb) { INIT(UV_FS_READ); - if (bufs == NULL || nbufs == 0) + if (bufs == NULL || nbufs == 0) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; + } req->file.fd = fd; @@ -2275,8 +2913,10 @@ int uv_fs_read(uv_loop_t* loop, if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); - if (req->fs.info.bufs == NULL) + if (req->fs.info.bufs == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); return UV_ENOMEM; + } memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); @@ -2294,8 +2934,10 @@ int uv_fs_write(uv_loop_t* loop, uv_fs_cb cb) { INIT(UV_FS_WRITE); - if (bufs == NULL || nbufs == 0) + if (bufs == NULL || nbufs == 0) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; + } req->file.fd = fd; @@ -2304,8 +2946,10 @@ int uv_fs_write(uv_loop_t* loop, if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); - if (req->fs.info.bufs == NULL) + if (req->fs.info.bufs == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); return UV_ENOMEM; + } memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); @@ -2321,7 +2965,8 @@ int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_UNLINK); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2335,7 +2980,8 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, INIT(UV_FS_MKDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.mode = mode; @@ -2343,14 +2989,35 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, } -int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, - uv_fs_cb cb) { +int uv_fs_mkdtemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb) { int err; INIT(UV_FS_MKDTEMP); err = fs__capture_path(req, tpl, NULL, TRUE); - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } + + POST; +} + + +int uv_fs_mkstemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb) { + int err; + + INIT(UV_FS_MKSTEMP); + err = fs__capture_path(req, tpl, NULL, TRUE); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } POST; } @@ -2362,7 +3029,8 @@ int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(UV_FS_RMDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2376,7 +3044,8 @@ int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, INIT(UV_FS_SCANDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.file_flags = flags; @@ -2391,8 +3060,10 @@ int uv_fs_opendir(uv_loop_t* loop, INIT(UV_FS_OPENDIR); err = fs__capture_path(req, path, NULL, cb != NULL); - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } POST; } @@ -2405,6 +3076,7 @@ int uv_fs_readdir(uv_loop_t* loop, if (dir == NULL || dir->dirents == NULL || dir->dir_handle == INVALID_HANDLE_VALUE) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; } @@ -2417,8 +3089,10 @@ int uv_fs_closedir(uv_loop_t* loop, uv_dir_t* dir, uv_fs_cb cb) { INIT(UV_FS_CLOSEDIR); - if (dir == NULL) + if (dir == NULL) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; + } req->ptr = dir; POST; } @@ -2430,7 +3104,8 @@ int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_LINK); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2444,7 +3119,8 @@ int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_SYMLINK); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.file_flags = flags; @@ -2459,7 +3135,8 @@ int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_READLINK); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2473,12 +3150,14 @@ int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_REALPATH); if (!path) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; } err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2492,7 +3171,8 @@ int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, INIT(UV_FS_CHOWN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2513,8 +3193,10 @@ int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, INIT(UV_FS_LCHOWN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } + POST; } @@ -2525,7 +3207,8 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(UV_FS_STAT); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2538,7 +3221,8 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(UV_FS_LSTAT); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2559,7 +3243,8 @@ int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_RENAME); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2602,13 +3287,15 @@ int uv_fs_copyfile(uv_loop_t* loop, if (flags & ~(UV_FS_COPYFILE_EXCL | UV_FS_COPYFILE_FICLONE | UV_FS_COPYFILE_FICLONE_FORCE)) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; } err = fs__capture_path(req, path, new_path, cb != NULL); - - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } req->fs.info.file_flags = flags; POST; @@ -2635,8 +3322,10 @@ int uv_fs_access(uv_loop_t* loop, INIT(UV_FS_ACCESS); err = fs__capture_path(req, path, NULL, cb != NULL); - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } req->fs.info.mode = flags; POST; @@ -2650,7 +3339,8 @@ int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, INIT(UV_FS_CHMOD); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.mode = mode; @@ -2674,7 +3364,8 @@ int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, INIT(UV_FS_UTIME); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.time.atime = atime; @@ -2691,3 +3382,40 @@ int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime, req->fs.time.mtime = mtime; POST; } + +int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, + double mtime, uv_fs_cb cb) { + int err; + + INIT(UV_FS_LUTIME); + err = fs__capture_path(req, path, NULL, cb != NULL); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } + + req->fs.time.atime = atime; + req->fs.time.mtime = mtime; + POST; +} + + +int uv_fs_statfs(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + int err; + + INIT(UV_FS_STATFS); + err = fs__capture_path(req, path, NULL, cb != NULL); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } + + POST; +} + +int uv_fs_get_system_error(const uv_fs_t* req) { + return req->sys_errno_; +} diff --git a/Utilities/cmlibuv/src/win/internal.h b/Utilities/cmlibuv/src/win/internal.h index f7d8ccfd94e..463ef33d635 100644 --- a/Utilities/cmlibuv/src/win/internal.h +++ b/Utilities/cmlibuv/src/win/internal.h @@ -284,6 +284,8 @@ int uv__getsockpeername(const uv_handle_t* handle, int* namelen, int delayed_error); +int uv__random_rtlgenrandom(void* buf, size_t buflen); + /* * Process stdio handles. diff --git a/Utilities/cmlibuv/src/win/pipe.c b/Utilities/cmlibuv/src/win/pipe.c index 277f6497a25..fc0112a33cf 100644 --- a/Utilities/cmlibuv/src/win/pipe.c +++ b/Utilities/cmlibuv/src/win/pipe.c @@ -264,8 +264,9 @@ static int uv_set_pipe_handle(uv_loop_t* loop, DWORD current_mode = 0; DWORD err = 0; - if (!(handle->flags & UV_HANDLE_PIPESERVER) && - handle->handle != INVALID_HANDLE_VALUE) + if (handle->flags & UV_HANDLE_PIPESERVER) + return UV_EINVAL; + if (handle->handle != INVALID_HANDLE_VALUE) return UV_EBUSY; if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) { @@ -312,7 +313,7 @@ static int uv_set_pipe_handle(uv_loop_t* loop, /* Overlapped pipe. Try to associate with IOCP. */ if (CreateIoCompletionPort(pipeHandle, loop->iocp, - (ULONG_PTR)handle, + (ULONG_PTR) handle, 0) == NULL) { handle->flags |= UV_HANDLE_EMULATE_IOCP; } @@ -326,6 +327,38 @@ static int uv_set_pipe_handle(uv_loop_t* loop, } +static int pipe_alloc_accept(uv_loop_t* loop, uv_pipe_t* handle, + uv_pipe_accept_t* req, BOOL firstInstance) { + assert(req->pipeHandle == INVALID_HANDLE_VALUE); + + req->pipeHandle = + CreateNamedPipeW(handle->name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC | + (firstInstance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0), + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); + + if (req->pipeHandle == INVALID_HANDLE_VALUE) { + return 0; + } + + /* Associate it with IOCP so we can get events. */ + if (CreateIoCompletionPort(req->pipeHandle, + loop->iocp, + (ULONG_PTR) handle, + 0) == NULL) { + uv_fatal_error(GetLastError(), "CreateIoCompletionPort"); + } + + /* Stash a handle in the server object for use from places such as + * getsockname and chmod. As we transfer ownership of these to client + * objects, we'll allocate new ones here. */ + handle->handle = req->pipeHandle; + + return 1; +} + + static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) { uv_loop_t* loop; uv_pipe_t* handle; @@ -458,7 +491,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { UnregisterWait(handle->read_req.wait_handle); handle->read_req.wait_handle = INVALID_HANDLE_VALUE; } - if (handle->read_req.event_handle) { + if (handle->read_req.event_handle != NULL) { CloseHandle(handle->read_req.event_handle); handle->read_req.event_handle = NULL; } @@ -540,13 +573,10 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { * Attempt to create the first pipe with FILE_FLAG_FIRST_PIPE_INSTANCE. * If this fails then there's already a pipe server for the given pipe name. */ - handle->pipe.serv.accept_reqs[0].pipeHandle = CreateNamedPipeW(handle->name, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | - FILE_FLAG_FIRST_PIPE_INSTANCE | WRITE_DAC, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); - - if (handle->pipe.serv.accept_reqs[0].pipeHandle == INVALID_HANDLE_VALUE) { + if (!pipe_alloc_accept(loop, + handle, + &handle->pipe.serv.accept_reqs[0], + TRUE)) { err = GetLastError(); if (err == ERROR_ACCESS_DENIED) { err = WSAEADDRINUSE; /* Translates to UV_EADDRINUSE. */ @@ -556,15 +586,6 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { goto error; } - if (uv_set_pipe_handle(loop, - handle, - handle->pipe.serv.accept_reqs[0].pipeHandle, - -1, - 0)) { - err = GetLastError(); - goto error; - } - handle->pipe.serv.pending_accepts = NULL; handle->flags |= UV_HANDLE_PIPESERVER; handle->flags |= UV_HANDLE_BOUND; @@ -577,11 +598,6 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { handle->name = NULL; } - if (handle->pipe.serv.accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE) { - CloseHandle(handle->pipe.serv.accept_reqs[0].pipeHandle); - handle->pipe.serv.accept_reqs[0].pipeHandle = INVALID_HANDLE_VALUE; - } - return uv_translate_sys_error(err); } @@ -827,29 +843,11 @@ static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, uv_pipe_accept_t* req, BOOL firstInstance) { assert(handle->flags & UV_HANDLE_LISTENING); - if (!firstInstance) { - assert(req->pipeHandle == INVALID_HANDLE_VALUE); - - req->pipeHandle = CreateNamedPipeW(handle->name, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); - - if (req->pipeHandle == INVALID_HANDLE_VALUE) { - SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*) req); - handle->reqs_pending++; - return; - } - - if (uv_set_pipe_handle(loop, handle, req->pipeHandle, -1, 0)) { - CloseHandle(req->pipeHandle); - req->pipeHandle = INVALID_HANDLE_VALUE; - SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*) req); - handle->reqs_pending++; - return; - } + if (!firstInstance && !pipe_alloc_accept(loop, handle, req, FALSE)) { + SET_REQ_ERROR(req, GetLastError()); + uv_insert_pending_req(loop, (uv_req_t*) req); + handle->reqs_pending++; + return; } assert(req->pipeHandle != INVALID_HANDLE_VALUE); @@ -904,7 +902,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { uv__free(item); } else { - pipe_client = (uv_pipe_t*)client; + pipe_client = (uv_pipe_t*) client; /* Find a connection instance that has been connected, but not yet * accepted. */ @@ -925,6 +923,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { req->next_pending = NULL; req->pipeHandle = INVALID_HANDLE_VALUE; + server->handle = INVALID_HANDLE_VALUE; if (!(server->flags & UV_HANDLE_CLOSING)) { uv_pipe_queue_accept(loop, server, req, FALSE); } @@ -955,6 +954,10 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { return ERROR_NOT_SUPPORTED; } + if (handle->ipc) { + return WSAEINVAL; + } + handle->flags |= UV_HANDLE_LISTENING; INCREASE_ACTIVE_COUNT(loop, handle); handle->stream.serv.connection_cb = cb; @@ -1131,6 +1134,7 @@ static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { } else { memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { + assert(req->event_handle != NULL); req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1); } @@ -1148,15 +1152,9 @@ static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { } if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - if (!req->event_handle) { - req->event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!req->event_handle) { - uv_fatal_error(GetLastError(), "CreateEvent"); - } - } if (req->wait_handle == INVALID_HANDLE_VALUE) { if (!RegisterWaitForSingleObject(&req->wait_handle, - req->u.io.overlapped.hEvent, post_completion_read_wait, (void*) req, + req->event_handle, post_completion_read_wait, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { SET_REQ_ERROR(req, GetLastError()); goto error; @@ -1190,8 +1188,16 @@ int uv_pipe_read_start(uv_pipe_t* handle, /* If reading was stopped and then started again, there could still be a read * request pending. */ - if (!(handle->flags & UV_HANDLE_READ_PENDING)) + if (!(handle->flags & UV_HANDLE_READ_PENDING)) { + if (handle->flags & UV_HANDLE_EMULATE_IOCP && + handle->read_req.event_handle == NULL) { + handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL); + if (handle->read_req.event_handle == NULL) { + uv_fatal_error(GetLastError(), "CreateEvent"); + } + } uv_pipe_queue_read(loop, handle); + } return 0; } @@ -1326,7 +1332,16 @@ static int uv__pipe_write_data(uv_loop_t* loop, req->coalesced = 0; req->event_handle = NULL; req->wait_handle = INVALID_HANDLE_VALUE; + + /* Prepare the overlapped structure. */ memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + if (handle->flags & (UV_HANDLE_EMULATE_IOCP | UV_HANDLE_BLOCKING_WRITES)) { + req->event_handle = CreateEvent(NULL, 0, 0, NULL); + if (req->event_handle == NULL) { + uv_fatal_error(GetLastError(), "CreateEvent"); + } + req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1); + } req->write_buffer = uv_null_buf_; if (nbufs == 0) { @@ -1375,11 +1390,6 @@ static int uv__pipe_write_data(uv_loop_t* loop, handle->write_queue_size += req->u.io.queued_bytes; } else if (handle->flags & UV_HANDLE_BLOCKING_WRITES) { /* Using overlapped IO, but wait for completion before returning */ - req->u.io.overlapped.hEvent = CreateEvent(NULL, 1, 0, NULL); - if (!req->u.io.overlapped.hEvent) { - uv_fatal_error(GetLastError(), "CreateEvent"); - } - result = WriteFile(handle->handle, write_buf.base, write_buf.len, @@ -1388,7 +1398,8 @@ static int uv__pipe_write_data(uv_loop_t* loop, if (!result && GetLastError() != ERROR_IO_PENDING) { err = GetLastError(); - CloseHandle(req->u.io.overlapped.hEvent); + CloseHandle(req->event_handle); + req->event_handle = NULL; return err; } @@ -1399,14 +1410,16 @@ static int uv__pipe_write_data(uv_loop_t* loop, /* Request queued by the kernel. */ req->u.io.queued_bytes = write_buf.len; handle->write_queue_size += req->u.io.queued_bytes; - if (WaitForSingleObject(req->u.io.overlapped.hEvent, INFINITE) != + if (WaitForSingleObject(req->event_handle, INFINITE) != WAIT_OBJECT_0) { err = GetLastError(); - CloseHandle(req->u.io.overlapped.hEvent); + CloseHandle(req->event_handle); + req->event_handle = NULL; return err; } } - CloseHandle(req->u.io.overlapped.hEvent); + CloseHandle(req->event_handle); + req->event_handle = NULL; REGISTER_HANDLE_REQ(loop, handle, req); handle->reqs_pending++; @@ -1433,12 +1446,8 @@ static int uv__pipe_write_data(uv_loop_t* loop, } if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - req->event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!req->event_handle) { - uv_fatal_error(GetLastError(), "CreateEvent"); - } if (!RegisterWaitForSingleObject(&req->wait_handle, - req->u.io.overlapped.hEvent, post_completion_write_wait, (void*) req, + req->event_handle, post_completion_write_wait, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { return GetLastError(); } diff --git a/Utilities/cmlibuv/src/win/poll.c b/Utilities/cmlibuv/src/win/poll.c index 3c6678600e4..87858590c85 100644 --- a/Utilities/cmlibuv/src/win/poll.c +++ b/Utilities/cmlibuv/src/win/poll.c @@ -134,32 +134,6 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { } -static int uv__fast_poll_cancel_poll_req(uv_loop_t* loop, uv_poll_t* handle) { - AFD_POLL_INFO afd_poll_info; - int result; - - afd_poll_info.Exclusive = TRUE; - afd_poll_info.NumberOfHandles = 1; - afd_poll_info.Timeout.QuadPart = INT64_MAX; - afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket; - afd_poll_info.Handles[0].Status = 0; - afd_poll_info.Handles[0].Events = AFD_POLL_ALL; - - result = uv_msafd_poll(handle->socket, - &afd_poll_info, - uv__get_afd_poll_info_dummy(), - uv__get_overlapped_dummy()); - - if (result == SOCKET_ERROR) { - DWORD error = WSAGetLastError(); - if (error != WSA_IO_PENDING) - return error; - } - - return 0; -} - - static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { unsigned char mask_events; @@ -226,44 +200,6 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, } -static int uv__fast_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { - assert(handle->type == UV_POLL); - assert(!(handle->flags & UV_HANDLE_CLOSING)); - assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0); - - handle->events = events; - - if (handle->events != 0) { - uv__handle_start(handle); - } else { - uv__handle_stop(handle); - } - - if ((handle->events & ~(handle->submitted_events_1 | - handle->submitted_events_2)) != 0) { - uv__fast_poll_submit_poll_req(handle->loop, handle); - } - - return 0; -} - - -static int uv__fast_poll_close(uv_loop_t* loop, uv_poll_t* handle) { - handle->events = 0; - uv__handle_closing(handle); - - if (handle->submitted_events_1 == 0 && - handle->submitted_events_2 == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); - return 0; - } else { - /* Cancel outstanding poll requests by executing another, unique poll - * request that forces the outstanding ones to return. */ - return uv__fast_poll_cancel_poll_req(loop, handle); - } -} - - static SOCKET uv__fast_poll_create_peer_socket(HANDLE iocp, WSAPROTOCOL_INFOW* protocol_info) { SOCKET sock = 0; @@ -469,41 +405,6 @@ static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, } -static int uv__slow_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { - assert(handle->type == UV_POLL); - assert(!(handle->flags & UV_HANDLE_CLOSING)); - assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0); - - handle->events = events; - - if (handle->events != 0) { - uv__handle_start(handle); - } else { - uv__handle_stop(handle); - } - - if ((handle->events & - ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { - uv__slow_poll_submit_poll_req(handle->loop, handle); - } - - return 0; -} - - -static int uv__slow_poll_close(uv_loop_t* loop, uv_poll_t* handle) { - handle->events = 0; - uv__handle_closing(handle); - - if (handle->submitted_events_1 == 0 && - handle->submitted_events_2 == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); - } - - return 0; -} - - int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { return uv_poll_init_socket(loop, handle, (SOCKET) uv__get_osfhandle(fd)); } @@ -582,35 +483,43 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, } -int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) { - int err; +static int uv__poll_set(uv_poll_t* handle, int events, uv_poll_cb cb) { + int submitted_events; - if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { - err = uv__fast_poll_set(handle->loop, handle, events); - } else { - err = uv__slow_poll_set(handle->loop, handle, events); - } + assert(handle->type == UV_POLL); + assert(!(handle->flags & UV_HANDLE_CLOSING)); + assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0); + + handle->events = events; + handle->poll_cb = cb; - if (err) { - return uv_translate_sys_error(err); + if (handle->events == 0) { + uv__handle_stop(handle); + return 0; } - handle->poll_cb = cb; + uv__handle_start(handle); + submitted_events = handle->submitted_events_1 | handle->submitted_events_2; + + if (handle->events & ~submitted_events) { + if (handle->flags & UV_HANDLE_POLL_SLOW) { + uv__slow_poll_submit_poll_req(handle->loop, handle); + } else { + uv__fast_poll_submit_poll_req(handle->loop, handle); + } + } return 0; } -int uv_poll_stop(uv_poll_t* handle) { - int err; +int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) { + return uv__poll_set(handle, events, cb); +} - if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { - err = uv__fast_poll_set(handle->loop, handle, 0); - } else { - err = uv__slow_poll_set(handle->loop, handle, 0); - } - return uv_translate_sys_error(err); +int uv_poll_stop(uv_poll_t* handle) { + return uv__poll_set(handle, 0, handle->poll_cb); } @@ -624,11 +533,43 @@ void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) { - if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { - return uv__fast_poll_close(loop, handle); - } else { - return uv__slow_poll_close(loop, handle); + AFD_POLL_INFO afd_poll_info; + DWORD error; + int result; + + handle->events = 0; + uv__handle_closing(handle); + + if (handle->submitted_events_1 == 0 && + handle->submitted_events_2 == 0) { + uv_want_endgame(loop, (uv_handle_t*) handle); + return 0; + } + + if (handle->flags & UV_HANDLE_POLL_SLOW) + return 0; + + /* Cancel outstanding poll requests by executing another, unique poll + * request that forces the outstanding ones to return. */ + afd_poll_info.Exclusive = TRUE; + afd_poll_info.NumberOfHandles = 1; + afd_poll_info.Timeout.QuadPart = INT64_MAX; + afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket; + afd_poll_info.Handles[0].Status = 0; + afd_poll_info.Handles[0].Events = AFD_POLL_ALL; + + result = uv_msafd_poll(handle->socket, + &afd_poll_info, + uv__get_afd_poll_info_dummy(), + uv__get_overlapped_dummy()); + + if (result == SOCKET_ERROR) { + error = WSAGetLastError(); + if (error != WSA_IO_PENDING) + return uv_translate_sys_error(error); } + + return 0; } diff --git a/Utilities/cmlibuv/src/win/process.c b/Utilities/cmlibuv/src/win/process.c index 24037b302c8..04718dbdb2f 100644 --- a/Utilities/cmlibuv/src/win/process.c +++ b/Utilities/cmlibuv/src/win/process.c @@ -58,7 +58,6 @@ static const env_var_t required_vars[] = { /* keep me sorted */ E_V("USERPROFILE"), E_V("WINDIR"), }; -static size_t n_required_vars = ARRAY_SIZE(required_vars); static HANDLE uv_global_job_handle_; @@ -692,7 +691,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { WCHAR* dst_copy; WCHAR** ptr_copy; WCHAR** env_copy; - DWORD* required_vars_value_len = alloca(n_required_vars * sizeof(DWORD*)); + DWORD required_vars_value_len[ARRAY_SIZE(required_vars)]; /* first pass: determine size in UTF-16 */ for (env = env_block; *env; env++) { @@ -714,7 +713,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { /* second pass: copy to UTF-16 environment block */ dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR)); - if (!dst_copy) { + if (dst_copy == NULL && env_len > 0) { return ERROR_OUTOFMEMORY; } env_copy = alloca(env_block_count * sizeof(WCHAR*)); @@ -739,13 +738,13 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { } } *ptr_copy = NULL; - assert(env_len == (size_t) (ptr - dst_copy)); + assert(env_len == 0 || env_len == (size_t) (ptr - dst_copy)); /* sort our (UTF-16) copy */ qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp); /* third pass: check for required variables */ - for (ptr_copy = env_copy, i = 0; i < n_required_vars; ) { + for (ptr_copy = env_copy, i = 0; i < ARRAY_SIZE(required_vars); ) { int cmp; if (!*ptr_copy) { cmp = -1; @@ -778,10 +777,10 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { } for (ptr = dst, ptr_copy = env_copy, i = 0; - *ptr_copy || i < n_required_vars; + *ptr_copy || i < ARRAY_SIZE(required_vars); ptr += len) { int cmp; - if (i >= n_required_vars) { + if (i >= ARRAY_SIZE(required_vars)) { cmp = 1; } else if (!*ptr_copy) { cmp = -1; diff --git a/Utilities/cmlibuv/src/win/signal.c b/Utilities/cmlibuv/src/win/signal.c index 276dc609733..3d9f92cfb17 100644 --- a/Utilities/cmlibuv/src/win/signal.c +++ b/Utilities/cmlibuv/src/win/signal.c @@ -46,6 +46,11 @@ void uv_signals_init(void) { } +void uv__signal_cleanup(void) { + /* TODO(bnoordhuis) Undo effects of uv_signal_init()? */ +} + + static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { /* Compare signums first so all watchers with the same signnum end up * adjacent. */ diff --git a/Utilities/cmlibuv/src/win/stream.c b/Utilities/cmlibuv/src/win/stream.c index 7656627e902..46a0709a38e 100644 --- a/Utilities/cmlibuv/src/win/stream.c +++ b/Utilities/cmlibuv/src/win/stream.c @@ -198,8 +198,10 @@ int uv_try_write(uv_stream_t* stream, int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { uv_loop_t* loop = handle->loop; - if (!(handle->flags & UV_HANDLE_WRITABLE)) { - return UV_EPIPE; + if (!(handle->flags & UV_HANDLE_WRITABLE) || + handle->flags & UV_HANDLE_SHUTTING || + uv__is_closing(handle)) { + return UV_ENOTCONN; } UV_REQ_INIT(req, UV_SHUTDOWN); @@ -207,6 +209,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { req->cb = cb; handle->flags &= ~UV_HANDLE_WRITABLE; + handle->flags |= UV_HANDLE_SHUTTING; handle->stream.conn.shutdown_req = req; handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); diff --git a/Utilities/cmlibuv/src/win/tcp.c b/Utilities/cmlibuv/src/win/tcp.c index f2cb5271b8d..941c8010d3f 100644 --- a/Utilities/cmlibuv/src/win/tcp.c +++ b/Utilities/cmlibuv/src/win/tcp.c @@ -251,7 +251,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { UnregisterWait(req->wait_handle); req->wait_handle = INVALID_HANDLE_VALUE; } - if (req->event_handle) { + if (req->event_handle != NULL) { CloseHandle(req->event_handle); req->event_handle = NULL; } @@ -268,7 +268,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { UnregisterWait(handle->read_req.wait_handle); handle->read_req.wait_handle = INVALID_HANDLE_VALUE; } - if (handle->read_req.event_handle) { + if (handle->read_req.event_handle != NULL) { CloseHandle(handle->read_req.event_handle); handle->read_req.event_handle = NULL; } @@ -428,6 +428,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { + assert(req->event_handle != NULL); req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1); } @@ -466,7 +467,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { closesocket(accept_socket); /* Destroy the event handle */ if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - CloseHandle(req->u.io.overlapped.hEvent); + CloseHandle(req->event_handle); req->event_handle = NULL; } } @@ -509,7 +510,7 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - assert(req->event_handle); + assert(req->event_handle != NULL); req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1); } @@ -549,6 +550,21 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { } +int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) { + struct linger l = { 1, 0 }; + + /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */ + if (handle->flags & UV_HANDLE_SHUTTING) + return UV_EINVAL; + + if (0 != setsockopt(handle->socket, SOL_SOCKET, SO_LINGER, (const char*)&l, sizeof(l))) + return uv_translate_sys_error(WSAGetLastError()); + + uv_close((uv_handle_t*) handle, close_cb); + return 0; +} + + int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { unsigned int i, simultaneous_accepts; uv_tcp_accept_t* req; @@ -597,8 +613,8 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { simultaneous_accepts = handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT ? 1 : uv_simultaneous_server_accepts; - if(!handle->tcp.serv.accept_reqs) { - handle->tcp.serv.accept_reqs = (uv_tcp_accept_t*) + if (handle->tcp.serv.accept_reqs == NULL) { + handle->tcp.serv.accept_reqs = uv__malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t)); if (!handle->tcp.serv.accept_reqs) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); @@ -613,7 +629,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { req->wait_handle = INVALID_HANDLE_VALUE; if (handle->flags & UV_HANDLE_EMULATE_IOCP) { req->event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!req->event_handle) { + if (req->event_handle == NULL) { uv_fatal_error(GetLastError(), "CreateEvent"); } } else { @@ -722,9 +738,9 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, * request pending. */ if (!(handle->flags & UV_HANDLE_READ_PENDING)) { if (handle->flags & UV_HANDLE_EMULATE_IOCP && - !handle->read_req.event_handle) { + handle->read_req.event_handle == NULL) { handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!handle->read_req.event_handle) { + if (handle->read_req.event_handle == NULL) { uv_fatal_error(GetLastError(), "CreateEvent"); } } @@ -847,7 +863,7 @@ int uv_tcp_write(uv_loop_t* loop, memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { req->event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!req->event_handle) { + if (req->event_handle == NULL) { uv_fatal_error(GetLastError(), "CreateEvent"); } req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1); @@ -1065,7 +1081,7 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, UnregisterWait(req->wait_handle); req->wait_handle = INVALID_HANDLE_VALUE; } - if (req->event_handle) { + if (req->event_handle != NULL) { CloseHandle(req->event_handle); req->event_handle = NULL; } diff --git a/Utilities/cmlibuv/src/win/tty.c b/Utilities/cmlibuv/src/win/tty.c index a98fe26335e..488d9b2a143 100644 --- a/Utilities/cmlibuv/src/win/tty.c +++ b/Utilities/cmlibuv/src/win/tty.c @@ -46,14 +46,16 @@ #define UNICODE_REPLACEMENT_CHARACTER (0xfffd) -#define ANSI_NORMAL 0x00 -#define ANSI_ESCAPE_SEEN 0x02 -#define ANSI_CSI 0x04 -#define ANSI_ST_CONTROL 0x08 -#define ANSI_IGNORE 0x10 -#define ANSI_IN_ARG 0x20 -#define ANSI_IN_STRING 0x40 -#define ANSI_BACKSLASH_SEEN 0x80 +#define ANSI_NORMAL 0x0000 +#define ANSI_ESCAPE_SEEN 0x0002 +#define ANSI_CSI 0x0004 +#define ANSI_ST_CONTROL 0x0008 +#define ANSI_IGNORE 0x0010 +#define ANSI_IN_ARG 0x0020 +#define ANSI_IN_STRING 0x0040 +#define ANSI_BACKSLASH_SEEN 0x0080 +#define ANSI_EXTENSION 0x0100 +#define ANSI_DECSCUSR 0x0200 #define MAX_INPUT_BUFFER_LENGTH 8192 #define MAX_CONSOLE_CHAR 8192 @@ -62,7 +64,12 @@ #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif -static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info); +#define CURSOR_SIZE_SMALL 25 +#define CURSOR_SIZE_LARGE 100 + +static void uv_tty_capture_initial_style( + CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info, + CONSOLE_CURSOR_INFO* cursor_info); static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info); static int uv__cancel_read_console(uv_tty_t* handle); @@ -120,6 +127,8 @@ static int uv_tty_virtual_width = -1; static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE; static int uv__tty_console_height = -1; static int uv__tty_console_width = -1; +static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE; +static uv_mutex_t uv__tty_console_resize_mutex; static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param); static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, @@ -129,6 +138,8 @@ static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime); +static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param); +static void uv__tty_console_signal_resize(void); /* We use a semaphore rather than a mutex or critical section because in some cases (uv__cancel_read_console) we need take the lock in the main thread and @@ -145,13 +156,11 @@ static char uv_tty_default_fg_bright = 0; static char uv_tty_default_bg_bright = 0; static char uv_tty_default_inverse = 0; -typedef enum { - UV_SUPPORTED, - UV_UNCHECKED, - UV_UNSUPPORTED -} uv_vtermstate_t; +static CONSOLE_CURSOR_INFO uv_tty_default_cursor_info; + /* Determine whether or not ANSI support is enabled. */ -static uv_vtermstate_t uv__vterm_state = UV_UNCHECKED; +static BOOL uv__need_check_vterm_state = TRUE; +static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED; static void uv__determine_vterm_state(HANDLE handle); void uv_console_init(void) { @@ -165,9 +174,15 @@ void uv_console_init(void) { 0, 0); if (uv__tty_console_handle != INVALID_HANDLE_VALUE) { + CONSOLE_SCREEN_BUFFER_INFO sb_info; QueueUserWorkItem(uv__tty_console_resize_message_loop_thread, NULL, WT_EXECUTELONGFUNCTION); + uv_mutex_init(&uv__tty_console_resize_mutex); + if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) { + uv__tty_console_width = sb_info.dwSize.X; + uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + } } } @@ -177,6 +192,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) { DWORD NumberOfEvents; HANDLE handle; CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; + CONSOLE_CURSOR_INFO cursor_info; (void)unused; uv__once_init(); @@ -209,15 +225,20 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) { return uv_translate_sys_error(GetLastError()); } + /* Obtain the cursor info with the output handle. */ + if (!GetConsoleCursorInfo(handle, &cursor_info)) { + return uv_translate_sys_error(GetLastError()); + } + /* Obtain the tty_output_lock because the virtual window state is shared * between all uv_tty_t handles. */ uv_sem_wait(&uv_tty_output_lock); - if (uv__vterm_state == UV_UNCHECKED) + if (uv__need_check_vterm_state) uv__determine_vterm_state(handle); - /* Remember the original console text attributes. */ - uv_tty_capture_initial_style(&screen_buffer_info); + /* Remember the original console text attributes and cursor info. */ + uv_tty_capture_initial_style(&screen_buffer_info, &cursor_info); uv_tty_update_virtual_window(&screen_buffer_info); @@ -268,7 +289,9 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) { /* Set the default console text attributes based on how the console was * configured when libuv started. */ -static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info) { +static void uv_tty_capture_initial_style( + CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info, + CONSOLE_CURSOR_INFO* cursor_info) { static int style_captured = 0; /* Only do this once. @@ -277,7 +300,7 @@ static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info) { return; /* Save raw win32 attributes. */ - uv_tty_default_text_attributes = info->wAttributes; + uv_tty_default_text_attributes = screen_buffer_info->wAttributes; /* Convert black text on black background to use white text. */ if (uv_tty_default_text_attributes == 0) @@ -317,6 +340,9 @@ static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info) { if (uv_tty_default_text_attributes & COMMON_LVB_REVERSE_VIDEO) uv_tty_default_inverse = 1; + /* Save the cursor size and the cursor state. */ + uv_tty_default_cursor_info = *cursor_info; + style_captured = 1; } @@ -728,6 +754,12 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, } records_left--; + /* We might be not subscribed to EVENT_CONSOLE_LAYOUT or we might be + * running under some TTY emulator that does not send those events. */ + if (handle->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) { + uv__tty_console_signal_resize(); + } + /* Ignore other events that are not key events. */ if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) { continue; @@ -1218,7 +1250,7 @@ static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative, static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { const COORD origin = {0, 0}; const WORD char_attrs = uv_tty_default_text_attributes; - CONSOLE_SCREEN_BUFFER_INFO info; + CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; DWORD count, written; if (*error != ERROR_SUCCESS) { @@ -1239,12 +1271,12 @@ static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { /* Clear the screen buffer. */ retry: - if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { - *error = GetLastError(); - return -1; + if (!GetConsoleScreenBufferInfo(handle->handle, &screen_buffer_info)) { + *error = GetLastError(); + return -1; } - count = info.dwSize.X * info.dwSize.Y; + count = screen_buffer_info.dwSize.X * screen_buffer_info.dwSize.Y; if (!(FillConsoleOutputCharacterW(handle->handle, L'\x20', @@ -1267,7 +1299,13 @@ static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { /* Move the virtual window up to the top. */ uv_tty_virtual_offset = 0; - uv_tty_update_virtual_window(&info); + uv_tty_update_virtual_window(&screen_buffer_info); + + /* Reset the cursor size and the cursor state. */ + if (!SetConsoleCursorInfo(handle->handle, &uv_tty_default_cursor_info)) { + *error = GetLastError(); + return -1; + } return 0; } @@ -1606,6 +1644,31 @@ static int uv_tty_set_cursor_visibility(uv_tty_t* handle, return 0; } +static int uv_tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) { + CONSOLE_CURSOR_INFO cursor_info; + + if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) { + *error = GetLastError(); + return -1; + } + + if (style == 0) { + cursor_info.dwSize = uv_tty_default_cursor_info.dwSize; + } else if (style <= 2) { + cursor_info.dwSize = CURSOR_SIZE_LARGE; + } else { + cursor_info.dwSize = CURSOR_SIZE_SMALL; + } + + if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) { + *error = GetLastError(); + return -1; + } + + return 0; +} + + static int uv_tty_write_bufs(uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs, @@ -1613,28 +1676,16 @@ static int uv_tty_write_bufs(uv_tty_t* handle, /* We can only write 8k characters at a time. Windows can't handle much more * characters in a single console write anyway. */ WCHAR utf16_buf[MAX_CONSOLE_CHAR]; - WCHAR* utf16_buffer; DWORD utf16_buf_used = 0; - unsigned int i, len, max_len, pos; - int allocate = 0; - -#define FLUSH_TEXT() \ - do { \ - pos = 0; \ - do { \ - len = utf16_buf_used - pos; \ - if (len > MAX_CONSOLE_CHAR) \ - len = MAX_CONSOLE_CHAR; \ - uv_tty_emit_text(handle, &utf16_buffer[pos], len, error); \ - pos += len; \ - } while (pos < utf16_buf_used); \ - if (allocate) { \ - uv__free(utf16_buffer); \ - allocate = 0; \ - utf16_buffer = utf16_buf; \ - } \ - utf16_buf_used = 0; \ - } while (0) + unsigned int i; + +#define FLUSH_TEXT() \ + do { \ + if (utf16_buf_used > 0) { \ + uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error); \ + utf16_buf_used = 0; \ + } \ + } while (0) #define ENSURE_BUFFER_SPACE(wchars_needed) \ if (wchars_needed > ARRAY_SIZE(utf16_buf) - utf16_buf_used) { \ @@ -1645,54 +1696,18 @@ static int uv_tty_write_bufs(uv_tty_t* handle, unsigned char utf8_bytes_left = handle->tty.wr.utf8_bytes_left; unsigned int utf8_codepoint = handle->tty.wr.utf8_codepoint; unsigned char previous_eol = handle->tty.wr.previous_eol; - unsigned char ansi_parser_state = handle->tty.wr.ansi_parser_state; + unsigned short ansi_parser_state = handle->tty.wr.ansi_parser_state; /* Store the error here. If we encounter an error, stop trying to do i/o but * keep parsing the buffer so we leave the parser in a consistent state. */ *error = ERROR_SUCCESS; - utf16_buffer = utf16_buf; - uv_sem_wait(&uv_tty_output_lock); for (i = 0; i < nbufs; i++) { uv_buf_t buf = bufs[i]; unsigned int j; - if (uv__vterm_state == UV_SUPPORTED && buf.len > 0) { - utf16_buf_used = MultiByteToWideChar(CP_UTF8, - 0, - buf.base, - buf.len, - NULL, - 0); - - if (utf16_buf_used == 0) { - *error = GetLastError(); - break; - } - - max_len = (utf16_buf_used + 1) * sizeof(WCHAR); - allocate = max_len > MAX_CONSOLE_CHAR; - if (allocate) - utf16_buffer = uv__malloc(max_len); - if (!MultiByteToWideChar(CP_UTF8, - 0, - buf.base, - buf.len, - utf16_buffer, - utf16_buf_used)) { - if (allocate) - uv__free(utf16_buffer); - *error = GetLastError(); - break; - } - - FLUSH_TEXT(); - - continue; - } - for (j = 0; j < buf.len; j++) { unsigned char c = buf.base[j]; @@ -1749,7 +1764,9 @@ static int uv_tty_write_bufs(uv_tty_t* handle, } /* Parse vt100/ansi escape codes */ - if (ansi_parser_state == ANSI_NORMAL) { + if (uv__vterm_state == UV_TTY_SUPPORTED) { + /* Pass through escape codes if conhost supports them. */ + } else if (ansi_parser_state == ANSI_NORMAL) { switch (utf8_codepoint) { case '\033': ansi_parser_state = ANSI_ESCAPE_SEEN; @@ -1795,7 +1812,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, ansi_parser_state = ANSI_NORMAL; continue; - case '8': + case '8': /* Restore the cursor position and text attributes */ FLUSH_TEXT(); uv_tty_restore_state(handle, 1, error); @@ -1813,121 +1830,193 @@ static int uv_tty_write_bufs(uv_tty_t* handle, } } + } else if (ansi_parser_state == ANSI_IGNORE) { + /* We're ignoring this command. Stop only on command character. */ + if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { + ansi_parser_state = ANSI_NORMAL; + } + continue; + + } else if (ansi_parser_state == ANSI_DECSCUSR) { + /* So far we've the sequence `ESC [ arg space`, and we're waiting for + * the final command byte. */ + if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { + /* Command byte */ + if (utf8_codepoint == 'q') { + /* Change the cursor shape */ + int style = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; + if (style >= 0 && style <= 6) { + FLUSH_TEXT(); + uv_tty_set_cursor_shape(handle, style, error); + } + } + + /* Sequence ended - go back to normal state. */ + ansi_parser_state = ANSI_NORMAL; + continue; + } + /* Unexpected character, but sequence hasn't ended yet. Ignore the rest + * of the sequence. */ + ansi_parser_state = ANSI_IGNORE; + } else if (ansi_parser_state & ANSI_CSI) { - if (!(ansi_parser_state & ANSI_IGNORE)) { - if (utf8_codepoint >= '0' && utf8_codepoint <= '9') { - /* Parsing a numerical argument */ - - if (!(ansi_parser_state & ANSI_IN_ARG)) { - /* We were not currently parsing a number */ - - /* Check for too many arguments */ - if (handle->tty.wr.ansi_csi_argc >= ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { - ansi_parser_state |= ANSI_IGNORE; - continue; - } - - ansi_parser_state |= ANSI_IN_ARG; - handle->tty.wr.ansi_csi_argc++; - handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = - (unsigned short) utf8_codepoint - '0'; + /* So far we've seen `ESC [`, and we may or may not have already parsed + * some of the arguments that follow. */ + + if (utf8_codepoint >= '0' && utf8_codepoint <= '9') { + /* Parse a numerical argument. */ + if (!(ansi_parser_state & ANSI_IN_ARG)) { + /* We were not currently parsing a number, add a new one. */ + /* Check for that there are too many arguments. */ + if (handle->tty.wr.ansi_csi_argc >= + ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { + ansi_parser_state = ANSI_IGNORE; continue; - } else { - /* We were already parsing a number. Parse next digit. */ - uint32_t value = 10 * - handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1]; - - /* Check for overflow. */ - if (value > UINT16_MAX) { - ansi_parser_state |= ANSI_IGNORE; - continue; - } - - handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = - (unsigned short) value + (utf8_codepoint - '0'); - continue; } + ansi_parser_state |= ANSI_IN_ARG; + handle->tty.wr.ansi_csi_argc++; + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = + (unsigned short) utf8_codepoint - '0'; + continue; + + } else { + /* We were already parsing a number. Parse next digit. */ + uint32_t value = 10 * + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1]; - } else if (utf8_codepoint == ';') { - /* Denotes the end of an argument. */ - if (ansi_parser_state & ANSI_IN_ARG) { - ansi_parser_state &= ~ANSI_IN_ARG; + /* Check for overflow. */ + if (value > UINT16_MAX) { + ansi_parser_state = ANSI_IGNORE; continue; + } - } else { - /* If ANSI_IN_ARG is not set, add another argument and default it - * to 0. */ + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = + (unsigned short) value + (utf8_codepoint - '0'); + continue; + } - /* Check for too many arguments */ - if (handle->tty.wr.ansi_csi_argc >= ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { - ansi_parser_state |= ANSI_IGNORE; - continue; - } + } else if (utf8_codepoint == ';') { + /* Denotes the end of an argument. */ + if (ansi_parser_state & ANSI_IN_ARG) { + ansi_parser_state &= ~ANSI_IN_ARG; + continue; + + } else { + /* If ANSI_IN_ARG is not set, add another argument and default + * it to 0. */ - handle->tty.wr.ansi_csi_argc++; - handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0; + /* Check for too many arguments */ + if (handle->tty.wr.ansi_csi_argc >= + + ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { + ansi_parser_state = ANSI_IGNORE; continue; } - } else if (utf8_codepoint == '?' && !(ansi_parser_state & ANSI_IN_ARG) && - handle->tty.wr.ansi_csi_argc == 0) { - /* Ignores '?' if it is the first character after CSI[. This is an - * extension character from the VT100 codeset that is supported and - * used by most ANSI terminals today. */ + handle->tty.wr.ansi_csi_argc++; + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0; continue; + } - } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~' && - (handle->tty.wr.ansi_csi_argc > 0 || utf8_codepoint != '[')) { - int x, y, d; + } else if (utf8_codepoint == '?' && + !(ansi_parser_state & ANSI_IN_ARG) && + !(ansi_parser_state & ANSI_EXTENSION) && + handle->tty.wr.ansi_csi_argc == 0) { + /* Pass through '?' if it is the first character after CSI */ + /* This is an extension character from the VT100 codeset */ + /* that is supported and used by most ANSI terminals today. */ + ansi_parser_state |= ANSI_EXTENSION; + continue; + + } else if (utf8_codepoint == ' ' && + !(ansi_parser_state & ANSI_EXTENSION)) { + /* We expect a command byte to follow after this space. The only + * command that we current support is 'set cursor style'. */ + ansi_parser_state = ANSI_DECSCUSR; + continue; - /* Command byte */ + } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { + /* Command byte */ + if (ansi_parser_state & ANSI_EXTENSION) { + /* Sequence is `ESC [ ? args command`. */ + switch (utf8_codepoint) { + case 'l': + /* Hide the cursor */ + if (handle->tty.wr.ansi_csi_argc == 1 && + handle->tty.wr.ansi_csi_argv[0] == 25) { + FLUSH_TEXT(); + uv_tty_set_cursor_visibility(handle, 0, error); + } + break; + + case 'h': + /* Show the cursor */ + if (handle->tty.wr.ansi_csi_argc == 1 && + handle->tty.wr.ansi_csi_argv[0] == 25) { + FLUSH_TEXT(); + uv_tty_set_cursor_visibility(handle, 1, error); + } + break; + } + + } else { + /* Sequence is `ESC [ args command`. */ + int x, y, d; switch (utf8_codepoint) { case 'A': /* cursor up */ FLUSH_TEXT(); - y = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); + y = -(handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1); uv_tty_move_caret(handle, 0, 1, y, 1, error); break; case 'B': /* cursor down */ FLUSH_TEXT(); - y = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; + y = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; uv_tty_move_caret(handle, 0, 1, y, 1, error); break; case 'C': /* cursor forward */ FLUSH_TEXT(); - x = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; + x = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; uv_tty_move_caret(handle, x, 1, 0, 1, error); break; case 'D': /* cursor back */ FLUSH_TEXT(); - x = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); + x = -(handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1); uv_tty_move_caret(handle, x, 1, 0, 1, error); break; case 'E': /* cursor next line */ FLUSH_TEXT(); - y = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; + y = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; uv_tty_move_caret(handle, 0, 0, y, 1, error); break; case 'F': /* cursor previous line */ FLUSH_TEXT(); - y = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); + y = -(handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1); uv_tty_move_caret(handle, 0, 0, y, 1, error); break; case 'G': /* cursor horizontal move absolute */ FLUSH_TEXT(); - x = (handle->tty.wr.ansi_csi_argc >= 1 && handle->tty.wr.ansi_csi_argv[0]) + x = (handle->tty.wr.ansi_csi_argc >= 1 && + handle->tty.wr.ansi_csi_argv[0]) ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0; uv_tty_move_caret(handle, x, 0, 0, 1, error); break; @@ -1936,9 +2025,11 @@ static int uv_tty_write_bufs(uv_tty_t* handle, case 'f': /* cursor move absolute */ FLUSH_TEXT(); - y = (handle->tty.wr.ansi_csi_argc >= 1 && handle->tty.wr.ansi_csi_argv[0]) + y = (handle->tty.wr.ansi_csi_argc >= 1 && + handle->tty.wr.ansi_csi_argv[0]) ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0; - x = (handle->tty.wr.ansi_csi_argc >= 2 && handle->tty.wr.ansi_csi_argv[1]) + x = (handle->tty.wr.ansi_csi_argc >= 2 && + handle->tty.wr.ansi_csi_argv[1]) ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0; uv_tty_move_caret(handle, x, 0, y, 0, error); break; @@ -1946,7 +2037,8 @@ static int uv_tty_write_bufs(uv_tty_t* handle, case 'J': /* Erase screen */ FLUSH_TEXT(); - d = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 0; + d = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 0; if (d >= 0 && d <= 2) { uv_tty_clear(handle, d, 1, error); } @@ -1955,7 +2047,8 @@ static int uv_tty_write_bufs(uv_tty_t* handle, case 'K': /* Erase line */ FLUSH_TEXT(); - d = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 0; + d = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 0; if (d >= 0 && d <= 2) { uv_tty_clear(handle, d, 0, error); } @@ -1978,41 +2071,17 @@ static int uv_tty_write_bufs(uv_tty_t* handle, FLUSH_TEXT(); uv_tty_restore_state(handle, 0, error); break; - - case 'l': - /* Hide the cursor */ - if (handle->tty.wr.ansi_csi_argc == 1 && - handle->tty.wr.ansi_csi_argv[0] == 25) { - FLUSH_TEXT(); - uv_tty_set_cursor_visibility(handle, 0, error); - } - break; - - case 'h': - /* Show the cursor */ - if (handle->tty.wr.ansi_csi_argc == 1 && - handle->tty.wr.ansi_csi_argv[0] == 25) { - FLUSH_TEXT(); - uv_tty_set_cursor_visibility(handle, 1, error); - } - break; } + } - /* Sequence ended - go back to normal state. */ - ansi_parser_state = ANSI_NORMAL; - continue; + /* Sequence ended - go back to normal state. */ + ansi_parser_state = ANSI_NORMAL; + continue; - } else { - /* We don't support commands that use private mode characters or - * intermediaries. Ignore the rest of the sequence. */ - ansi_parser_state |= ANSI_IGNORE; - continue; - } } else { - /* We're ignoring this command. Stop only on command character. */ - if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { - ansi_parser_state = ANSI_NORMAL; - } + /* We don't support commands that use private mode characters or + * intermediaries. Ignore the rest of the sequence. */ + ansi_parser_state = ANSI_IGNORE; continue; } @@ -2264,38 +2333,56 @@ int uv_tty_reset_mode(void) { static void uv__determine_vterm_state(HANDLE handle) { DWORD dwMode = 0; + uv__need_check_vterm_state = FALSE; if (!GetConsoleMode(handle, &dwMode)) { - uv__vterm_state = UV_UNSUPPORTED; return; } dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; if (!SetConsoleMode(handle, dwMode)) { - uv__vterm_state = UV_UNSUPPORTED; return; } - uv__vterm_state = UV_SUPPORTED; + uv__vterm_state = UV_TTY_SUPPORTED; } static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) { - CONSOLE_SCREEN_BUFFER_INFO sb_info; + NTSTATUS status; + ULONG_PTR conhost_pid; MSG msg; - if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) + if (pSetWinEventHook == NULL || pNtQueryInformationProcess == NULL) return 0; - uv__tty_console_width = sb_info.dwSize.X; - uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + status = pNtQueryInformationProcess(GetCurrentProcess(), + ProcessConsoleHostProcess, + &conhost_pid, + sizeof(conhost_pid), + NULL); - if (pSetWinEventHook == NULL) + if (!NT_SUCCESS(status)) { + /* We couldn't retrieve our console host process, probably because this + * is a 32-bit process running on 64-bit Windows. Fall back to receiving + * console events from the input stream only. */ + return 0; + } + + /* Ensure the PID is a multiple of 4, which is required by SetWinEventHook */ + conhost_pid &= ~(ULONG_PTR)0x3; + + uv__tty_console_resized = CreateEvent(NULL, TRUE, FALSE, NULL); + if (uv__tty_console_resized == NULL) + return 0; + if (QueueUserWorkItem(uv__tty_console_resize_watcher_thread, + NULL, + WT_EXECUTELONGFUNCTION) == 0) return 0; if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT, EVENT_CONSOLE_LAYOUT, NULL, uv__tty_console_resize_event, - 0, + (DWORD)conhost_pid, 0, WINEVENT_OUTOFCONTEXT)) return 0; @@ -2314,6 +2401,20 @@ static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime) { + SetEvent(uv__tty_console_resized); +} + +static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) { + for (;;) { + /* Make sure to not overwhelm the system with resize events */ + Sleep(33); + WaitForSingleObject(uv__tty_console_resized, INFINITE); + uv__tty_console_signal_resize(); + ResetEvent(uv__tty_console_resized); + } +} + +static void uv__tty_console_signal_resize(void) { CONSOLE_SCREEN_BUFFER_INFO sb_info; int width, height; @@ -2323,9 +2424,28 @@ static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, width = sb_info.dwSize.X; height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + uv_mutex_lock(&uv__tty_console_resize_mutex); + assert(uv__tty_console_width != -1 && uv__tty_console_height != -1); if (width != uv__tty_console_width || height != uv__tty_console_height) { uv__tty_console_width = width; uv__tty_console_height = height; + uv_mutex_unlock(&uv__tty_console_resize_mutex); uv__signal_dispatch(SIGWINCH); + } else { + uv_mutex_unlock(&uv__tty_console_resize_mutex); } } + +void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) { + uv_sem_wait(&uv_tty_output_lock); + uv__need_check_vterm_state = FALSE; + uv__vterm_state = state; + uv_sem_post(&uv_tty_output_lock); +} + +int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) { + uv_sem_wait(&uv_tty_output_lock); + *state = uv__vterm_state; + uv_sem_post(&uv_tty_output_lock); + return 0; +} diff --git a/Utilities/cmlibuv/src/win/udp.c b/Utilities/cmlibuv/src/win/udp.c index 8aeeab3b462..508ed37fb7b 100644 --- a/Utilities/cmlibuv/src/win/udp.c +++ b/Utilities/cmlibuv/src/win/udp.c @@ -125,17 +125,10 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, } -int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { - int domain; - - /* Use the lower 8 bits for the domain */ - domain = flags & 0xFF; - if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) - return UV_EINVAL; - - if (flags & ~0xFF) - return UV_EINVAL; - +int uv__udp_init_ex(uv_loop_t* loop, + uv_udp_t* handle, + unsigned flags, + int domain) { uv__handle_init(loop, (uv_handle_t*) handle, UV_UDP); handle->socket = INVALID_SOCKET; handle->reqs_pending = 0; @@ -174,11 +167,6 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { } -int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { - return uv_udp_init_ex(loop, handle, AF_UNSPEC); -} - - void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) { uv_udp_recv_stop(handle); closesocket(handle->socket); @@ -702,6 +690,112 @@ int uv__udp_set_membership6(uv_udp_t* handle, } +static int uv__udp_set_source_membership4(uv_udp_t* handle, + const struct sockaddr_in* multicast_addr, + const char* interface_addr, + const struct sockaddr_in* source_addr, + uv_membership membership) { + struct ip_mreq_source mreq; + int optname; + int err; + + if (handle->flags & UV_HANDLE_IPV6) + return UV_EINVAL; + + /* If the socket is unbound, bind to inaddr_any. */ + err = uv_udp_maybe_bind(handle, + (const struct sockaddr*) &uv_addr_ip4_any_, + sizeof(uv_addr_ip4_any_), + UV_UDP_REUSEADDR); + if (err) + return uv_translate_sys_error(err); + + memset(&mreq, 0, sizeof(mreq)); + + if (interface_addr != NULL) { + err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr); + if (err) + return err; + } else { + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + } + + mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; + mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr; + + if (membership == UV_JOIN_GROUP) + optname = IP_ADD_SOURCE_MEMBERSHIP; + else if (membership == UV_LEAVE_GROUP) + optname = IP_DROP_SOURCE_MEMBERSHIP; + else + return UV_EINVAL; + + if (setsockopt(handle->socket, + IPPROTO_IP, + optname, + (char*) &mreq, + sizeof(mreq)) == SOCKET_ERROR) { + return uv_translate_sys_error(WSAGetLastError()); + } + + return 0; +} + + +int uv__udp_set_source_membership6(uv_udp_t* handle, + const struct sockaddr_in6* multicast_addr, + const char* interface_addr, + const struct sockaddr_in6* source_addr, + uv_membership membership) { + struct group_source_req mreq; + struct sockaddr_in6 addr6; + int optname; + int err; + + if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6)) + return UV_EINVAL; + + err = uv_udp_maybe_bind(handle, + (const struct sockaddr*) &uv_addr_ip6_any_, + sizeof(uv_addr_ip6_any_), + UV_UDP_REUSEADDR); + + if (err) + return uv_translate_sys_error(err); + + memset(&mreq, 0, sizeof(mreq)); + + if (interface_addr != NULL) { + err = uv_ip6_addr(interface_addr, 0, &addr6); + if (err) + return err; + mreq.gsr_interface = addr6.sin6_scope_id; + } else { + mreq.gsr_interface = 0; + } + + memcpy(&mreq.gsr_group, multicast_addr, sizeof(mreq.gsr_group)); + memcpy(&mreq.gsr_source, source_addr, sizeof(mreq.gsr_source)); + + if (membership == UV_JOIN_GROUP) + optname = MCAST_JOIN_SOURCE_GROUP; + else if (membership == UV_LEAVE_GROUP) + optname = MCAST_LEAVE_SOURCE_GROUP; + else + return UV_EINVAL; + + if (setsockopt(handle->socket, + IPPROTO_IPV6, + optname, + (char*) &mreq, + sizeof(mreq)) == SOCKET_ERROR) { + return uv_translate_sys_error(WSAGetLastError()); + } + + return 0; +} + + int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, @@ -718,6 +812,50 @@ int uv_udp_set_membership(uv_udp_t* handle, } +int uv_udp_set_source_membership(uv_udp_t* handle, + const char* multicast_addr, + const char* interface_addr, + const char* source_addr, + uv_membership membership) { + int err; + struct sockaddr_storage mcast_addr; + struct sockaddr_in* mcast_addr4; + struct sockaddr_in6* mcast_addr6; + struct sockaddr_storage src_addr; + struct sockaddr_in* src_addr4; + struct sockaddr_in6* src_addr6; + + mcast_addr4 = (struct sockaddr_in*)&mcast_addr; + mcast_addr6 = (struct sockaddr_in6*)&mcast_addr; + src_addr4 = (struct sockaddr_in*)&src_addr; + src_addr6 = (struct sockaddr_in6*)&src_addr; + + err = uv_ip4_addr(multicast_addr, 0, mcast_addr4); + if (err) { + err = uv_ip6_addr(multicast_addr, 0, mcast_addr6); + if (err) + return err; + err = uv_ip6_addr(source_addr, 0, src_addr6); + if (err) + return err; + return uv__udp_set_source_membership6(handle, + mcast_addr6, + interface_addr, + src_addr6, + membership); + } + + err = uv_ip4_addr(source_addr, 0, src_addr4); + if (err) + return err; + return uv__udp_set_source_membership4(handle, + mcast_addr4, + interface_addr, + src_addr4, + membership); +} + + int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) { struct sockaddr_storage addr_st; struct sockaddr_in* addr4; diff --git a/Utilities/cmlibuv/src/win/util.c b/Utilities/cmlibuv/src/win/util.c index 7ca83213a67..9e1e7f73e31 100644 --- a/Utilities/cmlibuv/src/win/util.c +++ b/Utilities/cmlibuv/src/win/util.c @@ -60,8 +60,8 @@ #endif -/* Maximum environment variable size, including the terminating null */ -#define MAX_ENV_VAR_LENGTH 32767 +/* A RtlGenRandom() by any other name... */ +extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength); /* Cached copy of the process title, plus a mutex guarding it. */ static char *process_title; @@ -151,20 +151,26 @@ int uv_exepath(char* buffer, size_t* size_ptr) { int uv_cwd(char* buffer, size_t* size) { DWORD utf16_len; - WCHAR utf16_buffer[MAX_PATH]; + WCHAR *utf16_buffer; int r; if (buffer == NULL || size == NULL) { return UV_EINVAL; } - utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); + utf16_len = GetCurrentDirectoryW(0, NULL); if (utf16_len == 0) { return uv_translate_sys_error(GetLastError()); - } else if (utf16_len > MAX_PATH) { - /* This should be impossible; however the CRT has a code path to deal with - * this scenario, so I added a check anyway. */ - return UV_EIO; + } + utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR)); + if (utf16_buffer == NULL) { + return UV_ENOMEM; + } + + utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer); + if (utf16_len == 0) { + uv__free(utf16_buffer); + return uv_translate_sys_error(GetLastError()); } /* utf16_len contains the length, *not* including the terminating null. */ @@ -188,8 +194,10 @@ int uv_cwd(char* buffer, size_t* size) { NULL, NULL); if (r == 0) { + uv__free(utf16_buffer); return uv_translate_sys_error(GetLastError()); } else if (r > (int) *size) { + uv__free(utf16_buffer); *size = r; return UV_ENOBUFS; } @@ -203,6 +211,8 @@ int uv_cwd(char* buffer, size_t* size) { *size > INT_MAX ? INT_MAX : (int) *size, NULL, NULL); + uv__free(utf16_buffer); + if (r == 0) { return uv_translate_sys_error(GetLastError()); } @@ -213,43 +223,61 @@ int uv_cwd(char* buffer, size_t* size) { int uv_chdir(const char* dir) { - WCHAR utf16_buffer[MAX_PATH]; - size_t utf16_len; + WCHAR *utf16_buffer; + size_t utf16_len, new_utf16_len; WCHAR drive_letter, env_var[4]; if (dir == NULL) { return UV_EINVAL; } + utf16_len = MultiByteToWideChar(CP_UTF8, + 0, + dir, + -1, + NULL, + 0); + if (utf16_len == 0) { + return uv_translate_sys_error(GetLastError()); + } + utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR)); + if (utf16_buffer == NULL) { + return UV_ENOMEM; + } + if (MultiByteToWideChar(CP_UTF8, 0, dir, -1, utf16_buffer, - MAX_PATH) == 0) { - DWORD error = GetLastError(); - /* The maximum length of the current working directory is 260 chars, - * including terminating null. If it doesn't fit, the path name must be too - * long. */ - if (error == ERROR_INSUFFICIENT_BUFFER) { - return UV_ENAMETOOLONG; - } else { - return uv_translate_sys_error(error); - } + utf16_len) == 0) { + uv__free(utf16_buffer); + return uv_translate_sys_error(GetLastError()); } if (!SetCurrentDirectoryW(utf16_buffer)) { + uv__free(utf16_buffer); return uv_translate_sys_error(GetLastError()); } /* Windows stores the drive-local path in an "hidden" environment variable, * which has the form "=C:=C:\Windows". SetCurrentDirectory does not update * this, so we'll have to do it. */ - utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); + new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer); + if (new_utf16_len > utf16_len ) { + uv__free(utf16_buffer); + utf16_buffer = uv__malloc(new_utf16_len * sizeof(WCHAR)); + if (utf16_buffer == NULL) { + /* When updating the environment variable fails, return UV_OK anyway. + * We did successfully change current working directory, only updating + * hidden env variable failed. */ + return 0; + } + new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer); + } if (utf16_len == 0) { - return uv_translate_sys_error(GetLastError()); - } else if (utf16_len > MAX_PATH) { - return UV_EIO; + uv__free(utf16_buffer); + return 0; } /* The returned directory should not have a trailing slash, unless it points @@ -281,11 +309,10 @@ int uv_chdir(const char* dir) { env_var[2] = L':'; env_var[3] = L'\0'; - if (!SetEnvironmentVariableW(env_var, utf16_buffer)) { - return uv_translate_sys_error(GetLastError()); - } + SetEnvironmentVariableW(env_var, utf16_buffer); } + uv__free(utf16_buffer); return 0; } @@ -358,6 +385,10 @@ char** uv_setup_args(int argc, char** argv) { } +void uv__process_title_cleanup(void) { +} + + int uv_set_process_title(const char* title) { int err; int length; @@ -721,17 +752,6 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { } -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; - - for (i = 0; i < count; i++) { - uv__free(cpu_infos[i].model); - } - - uv__free(cpu_infos); -} - - static int is_windows_version_or_greater(DWORD os_major, DWORD os_minor, WORD service_pack_major, @@ -1171,20 +1191,29 @@ int uv_os_homedir(char* buffer, size_t* size) { int uv_os_tmpdir(char* buffer, size_t* size) { - wchar_t path[MAX_PATH + 1]; + wchar_t *path; DWORD bufsize; size_t len; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - len = GetTempPathW(MAX_PATH + 1, path); + len = 0; + len = GetTempPathW(0, NULL); + if (len == 0) { + return uv_translate_sys_error(GetLastError()); + } + /* Include space for terminating null char. */ + len += 1; + path = uv__malloc(len * sizeof(wchar_t)); + if (path == NULL) { + return UV_ENOMEM; + } + len = GetTempPathW(len, path); if (len == 0) { + uv__free(path); return uv_translate_sys_error(GetLastError()); - } else if (len > MAX_PATH + 1) { - /* This should not be possible */ - return UV_EIO; } /* The returned directory should not have a trailing slash, unless it points @@ -1199,8 +1228,10 @@ int uv_os_tmpdir(char* buffer, size_t* size) { bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL); if (bufsize == 0) { + uv__free(path); return uv_translate_sys_error(GetLastError()); } else if (bufsize > *size) { + uv__free(path); *size = bufsize; return UV_ENOBUFS; } @@ -1214,6 +1245,7 @@ int uv_os_tmpdir(char* buffer, size_t* size) { *size, NULL, NULL); + uv__free(path); if (bufsize == 0) return uv_translate_sys_error(GetLastError()); @@ -1325,7 +1357,7 @@ int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) { return uv_translate_sys_error(GetLastError()); } - (*utf16)[bufsize] = '\0'; + (*utf16)[bufsize] = L'\0'; return 0; } @@ -1333,7 +1365,7 @@ int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) { int uv__getpwuid_r(uv_passwd_t* pwd) { HANDLE token; wchar_t username[UNLEN + 1]; - wchar_t path[MAX_PATH]; + wchar_t *path; DWORD bufsize; int r; @@ -1344,15 +1376,24 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0) return uv_translate_sys_error(GetLastError()); - bufsize = ARRAY_SIZE(path); - if (!GetUserProfileDirectoryW(token, path, &bufsize)) { + bufsize = 0; + GetUserProfileDirectoryW(token, NULL, &bufsize); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { r = GetLastError(); CloseHandle(token); + return uv_translate_sys_error(r); + } - /* This should not be possible */ - if (r == ERROR_INSUFFICIENT_BUFFER) - return UV_ENOMEM; + path = uv__malloc(bufsize * sizeof(wchar_t)); + if (path == NULL) { + CloseHandle(token); + return UV_ENOMEM; + } + if (!GetUserProfileDirectoryW(token, path, &bufsize)) { + r = GetLastError(); + CloseHandle(token); + uv__free(path); return uv_translate_sys_error(r); } @@ -1362,6 +1403,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { bufsize = ARRAY_SIZE(username); if (!GetUserNameW(username, &bufsize)) { r = GetLastError(); + uv__free(path); /* This should not be possible */ if (r == ERROR_INSUFFICIENT_BUFFER) @@ -1372,6 +1414,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { pwd->homedir = NULL; r = uv__convert_utf16_to_utf8(path, -1, &pwd->homedir); + uv__free(path); if (r != 0) return r; @@ -1397,8 +1440,81 @@ int uv_os_get_passwd(uv_passwd_t* pwd) { } +int uv_os_environ(uv_env_item_t** envitems, int* count) { + wchar_t* env; + wchar_t* penv; + int i, cnt; + uv_env_item_t* envitem; + + *envitems = NULL; + *count = 0; + + env = GetEnvironmentStringsW(); + if (env == NULL) + return 0; + + for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++); + + *envitems = uv__calloc(i, sizeof(**envitems)); + if (*envitems == NULL) { + FreeEnvironmentStringsW(env); + return UV_ENOMEM; + } + + penv = env; + cnt = 0; + + while (*penv != L'\0' && cnt < i) { + char* buf; + char* ptr; + + if (uv__convert_utf16_to_utf8(penv, -1, &buf) != 0) + goto fail; + + /* Using buf + 1 here because we know that `buf` has length at least 1, + * and some special environment variables on Windows start with a = sign. */ + ptr = strchr(buf + 1, '='); + if (ptr == NULL) { + uv__free(buf); + goto do_continue; + } + + *ptr = '\0'; + + envitem = &(*envitems)[cnt]; + envitem->name = buf; + envitem->value = ptr + 1; + + cnt++; + + do_continue: + penv += wcslen(penv) + 1; + } + + FreeEnvironmentStringsW(env); + + *count = cnt; + return 0; + +fail: + FreeEnvironmentStringsW(env); + + for (i = 0; i < cnt; i++) { + envitem = &(*envitems)[cnt]; + uv__free(envitem->name); + } + uv__free(*envitems); + + *envitems = NULL; + *count = 0; + return UV_ENOMEM; +} + + int uv_os_getenv(const char* name, char* buffer, size_t* size) { - wchar_t var[MAX_ENV_VAR_LENGTH]; + wchar_t fastvar[512]; + wchar_t* var; + DWORD varlen; wchar_t* name_w; DWORD bufsize; size_t len; @@ -1412,27 +1528,52 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) { if (r != 0) return r; - len = GetEnvironmentVariableW(name_w, var, MAX_ENV_VAR_LENGTH); + var = fastvar; + varlen = ARRAY_SIZE(fastvar); + + for (;;) { + SetLastError(ERROR_SUCCESS); + len = GetEnvironmentVariableW(name_w, var, varlen); + + if (len < varlen) + break; + + /* Try repeatedly because we might have been preempted by another thread + * modifying the environment variable just as we're trying to read it. + */ + if (var != fastvar) + uv__free(var); + + varlen = 1 + len; + var = uv__malloc(varlen * sizeof(*var)); + + if (var == NULL) { + r = UV_ENOMEM; + goto fail; + } + } + uv__free(name_w); - assert(len < MAX_ENV_VAR_LENGTH); /* len does not include the null */ + name_w = NULL; if (len == 0) { r = GetLastError(); - - if (r == ERROR_ENVVAR_NOT_FOUND) - return UV_ENOENT; - - return uv_translate_sys_error(r); + if (r != ERROR_SUCCESS) { + r = uv_translate_sys_error(r); + goto fail; + } } /* Check how much space we need */ bufsize = WideCharToMultiByte(CP_UTF8, 0, var, -1, NULL, 0, NULL, NULL); if (bufsize == 0) { - return uv_translate_sys_error(GetLastError()); + r = uv_translate_sys_error(GetLastError()); + goto fail; } else if (bufsize > *size) { *size = bufsize; - return UV_ENOBUFS; + r = UV_ENOBUFS; + goto fail; } /* Convert to UTF-8 */ @@ -1445,11 +1586,23 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) { NULL, NULL); - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); + if (bufsize == 0) { + r = uv_translate_sys_error(GetLastError()); + goto fail; + } *size = bufsize - 1; - return 0; + r = 0; + +fail: + + if (name_w != NULL) + uv__free(name_w); + + if (var != fastvar) + uv__free(var); + + return r; } @@ -1802,3 +1955,17 @@ int uv_gettimeofday(uv_timeval64_t* tv) { tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10); return 0; } + +int uv__random_rtlgenrandom(void* buf, size_t buflen) { + if (buflen == 0) + return 0; + + if (SystemFunction036(buf, buflen) == FALSE) + return UV_EIO; + + return 0; +} + +void uv_sleep(unsigned int msec) { + Sleep(msec); +} diff --git a/Utilities/cmlibuv/src/win/winapi.c b/Utilities/cmlibuv/src/win/winapi.c index fbbbceed95e..bb86ec8ceac 100644 --- a/Utilities/cmlibuv/src/win/winapi.c +++ b/Utilities/cmlibuv/src/win/winapi.c @@ -34,6 +34,7 @@ sNtSetInformationFile pNtSetInformationFile; sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile; sNtQueryDirectoryFile pNtQueryDirectoryFile; sNtQuerySystemInformation pNtQuerySystemInformation; +sNtQueryInformationProcess pNtQueryInformationProcess; /* Kernel32 function pointers */ sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; @@ -106,6 +107,13 @@ void uv_winapi_init(void) { uv_fatal_error(GetLastError(), "GetProcAddress"); } + pNtQueryInformationProcess = (sNtQueryInformationProcess) GetProcAddress( + ntdll_module, + "NtQueryInformationProcess"); + if (pNtQueryInformationProcess == NULL) { + uv_fatal_error(GetLastError(), "GetProcAddress"); + } + kernel32_module = GetModuleHandleA("kernel32.dll"); if (kernel32_module == NULL) { uv_fatal_error(GetLastError(), "GetModuleHandleA"); @@ -126,5 +134,4 @@ void uv_winapi_init(void) { pSetWinEventHook = (sSetWinEventHook) GetProcAddress(user32_module, "SetWinEventHook"); } - } diff --git a/Utilities/cmlibuv/src/win/winapi.h b/Utilities/cmlibuv/src/win/winapi.h index 1e49b578732..77f1878716d 100644 --- a/Utilities/cmlibuv/src/win/winapi.h +++ b/Utilities/cmlibuv/src/win/winapi.h @@ -4109,7 +4109,7 @@ #endif /* from winternl.h */ -#if !defined(__UNICODE_STRING_DEFINED) && defined(__MINGW32_) +#if !defined(__UNICODE_STRING_DEFINED) && defined(__MINGW32__) #define __UNICODE_STRING_DEFINED #endif typedef struct _UNICODE_STRING { @@ -4160,6 +4160,10 @@ typedef const UNICODE_STRING *PCUNICODE_STRING; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; + struct { + ULONG StringCount; + WCHAR StringList[1]; + } AppExecLinkReparseBuffer; }; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; #endif @@ -4444,6 +4448,10 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { # define SystemProcessorPerformanceInformation 8 #endif +#ifndef ProcessConsoleHostProcess +# define ProcessConsoleHostProcess 49 +#endif + #ifndef FILE_DEVICE_FILE_SYSTEM # define FILE_DEVICE_FILE_SYSTEM 0x00000009 #endif @@ -4521,6 +4529,9 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { #ifndef IO_REPARSE_TAG_SYMLINK # define IO_REPARSE_TAG_SYMLINK (0xA000000CL) #endif +#ifndef IO_REPARSE_TAG_APPEXECLINK +# define IO_REPARSE_TAG_APPEXECLINK (0x8000001BL) +#endif typedef VOID (NTAPI *PIO_APC_ROUTINE) (PVOID ApcContext, @@ -4586,6 +4597,13 @@ typedef NTSTATUS (NTAPI *sNtQueryDirectoryFile) BOOLEAN RestartScan ); +typedef NTSTATUS (NTAPI *sNtQueryInformationProcess) + (HANDLE ProcessHandle, + UINT ProcessInformationClass, + PVOID ProcessInformation, + ULONG Length, + PULONG ReturnLength); + /* * Kernel32 headers */ @@ -4727,6 +4745,7 @@ extern sNtSetInformationFile pNtSetInformationFile; extern sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile; extern sNtQueryDirectoryFile pNtQueryDirectoryFile; extern sNtQuerySystemInformation pNtQuerySystemInformation; +extern sNtQueryInformationProcess pNtQueryInformationProcess; /* Kernel32 function pointers */ extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; diff --git a/Utilities/cmlibuv/src/win/winsock.c b/Utilities/cmlibuv/src/win/winsock.c index 5820ba9c66d..4cf6e6b042c 100644 --- a/Utilities/cmlibuv/src/win/winsock.c +++ b/Utilities/cmlibuv/src/win/winsock.c @@ -74,11 +74,6 @@ BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) { } -static int error_means_no_support(DWORD error) { - return error == WSAEPROTONOSUPPORT || error == WSAESOCKTNOSUPPORT || - error == WSAEPFNOSUPPORT || error == WSAEAFNOSUPPORT; -} - void uv_winsock_init(void) { WSADATA wsa_data; @@ -105,50 +100,36 @@ void uv_winsock_init(void) { uv_fatal_error(errorno, "WSAStartup"); } - /* Detect non-IFS LSPs */ + /* Try to detect non-IFS LSPs */ + uv_tcp_non_ifs_lsp_ipv4 = 1; dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - if (dummy != INVALID_SOCKET) { opt_len = (int) sizeof protocol_info; if (getsockopt(dummy, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &protocol_info, - &opt_len) == SOCKET_ERROR) - uv_fatal_error(WSAGetLastError(), "getsockopt"); - - if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)) - uv_tcp_non_ifs_lsp_ipv4 = 1; - - if (closesocket(dummy) == SOCKET_ERROR) - uv_fatal_error(WSAGetLastError(), "closesocket"); - - } else if (!error_means_no_support(WSAGetLastError())) { - /* Any error other than "socket type not supported" is fatal. */ - uv_fatal_error(WSAGetLastError(), "socket"); + &opt_len) == 0) { + if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES) + uv_tcp_non_ifs_lsp_ipv4 = 0; + } + closesocket(dummy); } - /* Detect IPV6 support and non-IFS LSPs */ + /* Try to detect IPV6 support and non-IFS LSPs */ + uv_tcp_non_ifs_lsp_ipv6 = 1; dummy = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP); - if (dummy != INVALID_SOCKET) { opt_len = (int) sizeof protocol_info; if (getsockopt(dummy, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &protocol_info, - &opt_len) == SOCKET_ERROR) - uv_fatal_error(WSAGetLastError(), "getsockopt"); - - if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)) - uv_tcp_non_ifs_lsp_ipv6 = 1; - - if (closesocket(dummy) == SOCKET_ERROR) - uv_fatal_error(WSAGetLastError(), "closesocket"); - - } else if (!error_means_no_support(WSAGetLastError())) { - /* Any error other than "socket type not supported" is fatal. */ - uv_fatal_error(WSAGetLastError(), "socket"); + &opt_len) == 0) { + if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES) + uv_tcp_non_ifs_lsp_ipv6 = 0; + } + closesocket(dummy); } } diff --git a/Utilities/cmlibuv/src/win/winsock.h b/Utilities/cmlibuv/src/win/winsock.h index e8b274e1bef..153632c1892 100644 --- a/Utilities/cmlibuv/src/win/winsock.h +++ b/Utilities/cmlibuv/src/win/winsock.h @@ -54,6 +54,14 @@ # define SIO_BASE_HANDLE 0x48000022 #endif +#ifndef MCAST_JOIN_SOURCE_GROUP +# define MCAST_JOIN_SOURCE_GROUP 45 +#endif + +#ifndef MCAST_LEAVE_SOURCE_GROUP +# define MCAST_LEAVE_SOURCE_GROUP 46 +#endif + /* * TDI defines that are only in the DDK. * We only need receive flags so far. diff --git a/Utilities/cmnghttp2/.gitattributes b/Utilities/cmnghttp2/.gitattributes new file mode 100644 index 00000000000..562b12e16eb --- /dev/null +++ b/Utilities/cmnghttp2/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/Utilities/cmnghttp2/CMakeLists.txt b/Utilities/cmnghttp2/CMakeLists.txt new file mode 100644 index 00000000000..3a11acec60a --- /dev/null +++ b/Utilities/cmnghttp2/CMakeLists.txt @@ -0,0 +1,52 @@ +# Disable warnings to avoid changing 3rd party code. +if(CMAKE_C_COMPILER_ID MATCHES + "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w") +elseif(CMAKE_C_COMPILER_ID STREQUAL "PathScale") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall") +endif() + +# Re-use some check result cache entries from cmcurl: +# * HAVE_ARPA_INET_H +# * HAVE_NETINET_IN_H +# * HAVE_SSIZE_T +if(NOT HAVE_SSIZE_T) + set(ssize_t KWIML_INT_intptr_t) +endif() +configure_file(cmakeconfig.h.in config.h) + +add_library(cmnghttp2 STATIC + lib/nghttp2_buf.c + lib/nghttp2_callbacks.c + lib/nghttp2_debug.c + lib/nghttp2_frame.c + lib/nghttp2_hd.c + lib/nghttp2_hd_huffman.c + lib/nghttp2_hd_huffman_data.c + lib/nghttp2_helper.c + lib/nghttp2_http.c + lib/nghttp2_map.c + lib/nghttp2_mem.c + lib/nghttp2_npn.c + lib/nghttp2_option.c + lib/nghttp2_outbound_item.c + lib/nghttp2_pq.c + lib/nghttp2_priority_spec.c + lib/nghttp2_queue.c + lib/nghttp2_rcbuf.c + lib/nghttp2_session.c + lib/nghttp2_stream.c + lib/nghttp2_submit.c + lib/nghttp2_version.c + ) + +target_compile_definitions(cmnghttp2 + PUBLIC NGHTTP2_STATICLIB + PRIVATE HAVE_CONFIG_H + ) +target_include_directories(cmnghttp2 PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/lib/includes + ) + +install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmnghttp2) diff --git a/Utilities/cmnghttp2/COPYING b/Utilities/cmnghttp2/COPYING new file mode 100644 index 00000000000..80201792ec7 --- /dev/null +++ b/Utilities/cmnghttp2/COPYING @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa +Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Utilities/cmnghttp2/cmakeconfig.h.in b/Utilities/cmnghttp2/cmakeconfig.h.in new file mode 100644 index 00000000000..60698fb99b8 --- /dev/null +++ b/Utilities/cmnghttp2/cmakeconfig.h.in @@ -0,0 +1,18 @@ +#if defined(_MSC_VER) +# pragma warning(push,1) +#endif + +#include +#include + +/* Define to `int' if does not define. */ +#cmakedefine ssize_t @ssize_t@ + +/* sizeof(int *) */ +#define SIZEOF_INT_P KWIML_ABI_SIZEOF_DATA_PTR + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETINET_IN_H 1 diff --git a/Utilities/cmnghttp2/lib/includes/nghttp2/nghttp2.h b/Utilities/cmnghttp2/lib/includes/nghttp2/nghttp2.h new file mode 100644 index 00000000000..e3aeb9fed31 --- /dev/null +++ b/Utilities/cmnghttp2/lib/includes/nghttp2/nghttp2.h @@ -0,0 +1,5487 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013, 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_H +#define NGHTTP2_H + +/* Define WIN32 when build target is Win32 API (borrowed from + libcurl) */ +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif + +/* Compatibility for non-Clang compilers */ +#ifndef __has_declspec_attribute +# define __has_declspec_attribute(x) 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 + compliant. See compiler macros and version number in + https://sourceforge.net/p/predef/wiki/Compilers/ */ +# include +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +# include +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include +#include + +#include + +#ifdef NGHTTP2_STATICLIB +# define NGHTTP2_EXTERN +#elif defined(WIN32) || (__has_declspec_attribute(dllexport) && \ + __has_declspec_attribute(dllimport)) +# ifdef BUILDING_NGHTTP2 +# define NGHTTP2_EXTERN __declspec(dllexport) +# else /* !BUILDING_NGHTTP2 */ +# define NGHTTP2_EXTERN __declspec(dllimport) +# endif /* !BUILDING_NGHTTP2 */ +#else /* !defined(WIN32) */ +# ifdef BUILDING_NGHTTP2 +# define NGHTTP2_EXTERN __attribute__((visibility("default"))) +# else /* !BUILDING_NGHTTP2 */ +# define NGHTTP2_EXTERN +# endif /* !BUILDING_NGHTTP2 */ +#endif /* !defined(WIN32) */ + +/** + * @macro + * + * The protocol version identification string of this library + * supports. This identifier is used if HTTP/2 is used over TLS. + */ +#define NGHTTP2_PROTO_VERSION_ID "h2" +/** + * @macro + * + * The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. + */ +#define NGHTTP2_PROTO_VERSION_ID_LEN 2 + +/** + * @macro + * + * The serialized form of ALPN protocol identifier this library + * supports. Notice that first byte is the length of following + * protocol identifier. This is the same wire format of `TLS ALPN + * extension `_. This is useful + * to process incoming ALPN tokens in wire format. + */ +#define NGHTTP2_PROTO_ALPN "\x2h2" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_PROTO_ALPN`. + */ +#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1) + +/** + * @macro + * + * The protocol version identification string of this library + * supports. This identifier is used if HTTP/2 is used over cleartext + * TCP. + */ +#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. + */ +#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3 + +struct nghttp2_session; +/** + * @struct + * + * The primary structure to hold the resources needed for a HTTP/2 + * session. The details of this structure are intentionally hidden + * from the public API. + */ +typedef struct nghttp2_session nghttp2_session; + +/** + * @macro + * + * The age of :type:`nghttp2_info` + */ +#define NGHTTP2_VERSION_AGE 1 + +/** + * @struct + * + * This struct is what `nghttp2_version()` returns. It holds + * information about the particular nghttp2 version. + */ +typedef struct { + /** + * Age of this struct. This instance of nghttp2 sets it to + * :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and + * add more struct fields at the bottom + */ + int age; + /** + * the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) + */ + int version_num; + /** + * points to the :macro:`NGHTTP2_VERSION` string (since age ==1) + */ + const char *version_str; + /** + * points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this + * instance implements (since age ==1) + */ + const char *proto_str; + /* -------- the above fields all exist when age == 1 */ +} nghttp2_info; + +/** + * @macro + * + * The default weight of stream dependency. + */ +#define NGHTTP2_DEFAULT_WEIGHT 16 + +/** + * @macro + * + * The maximum weight of stream dependency. + */ +#define NGHTTP2_MAX_WEIGHT 256 + +/** + * @macro + * + * The minimum weight of stream dependency. + */ +#define NGHTTP2_MIN_WEIGHT 1 + +/** + * @macro + * + * The maximum window size + */ +#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1)) + +/** + * @macro + * + * The initial window size for stream level flow control. + */ +#define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1) +/** + * @macro + * + * The initial window size for connection level flow control. + */ +#define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1) + +/** + * @macro + * + * The default header table size. + */ +#define NGHTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) + +/** + * @macro + * + * The client magic string, which is the first 24 bytes byte string of + * client connection preface. + */ +#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_CLIENT_MAGIC`. + */ +#define NGHTTP2_CLIENT_MAGIC_LEN 24 + +/** + * @enum + * + * Error codes used in this library. The code range is [-999, -500], + * inclusive. The following values are defined: + */ +typedef enum { + /** + * Invalid argument passed. + */ + NGHTTP2_ERR_INVALID_ARGUMENT = -501, + /** + * Out of buffer space. + */ + NGHTTP2_ERR_BUFFER_ERROR = -502, + /** + * The specified protocol version is not supported. + */ + NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, + /** + * Used as a return value from :type:`nghttp2_send_callback`, + * :type:`nghttp2_recv_callback` and + * :type:`nghttp2_send_data_callback` to indicate that the operation + * would block. + */ + NGHTTP2_ERR_WOULDBLOCK = -504, + /** + * General protocol error + */ + NGHTTP2_ERR_PROTO = -505, + /** + * The frame is invalid. + */ + NGHTTP2_ERR_INVALID_FRAME = -506, + /** + * The peer performed a shutdown on the connection. + */ + NGHTTP2_ERR_EOF = -507, + /** + * Used as a return value from + * :func:`nghttp2_data_source_read_callback` to indicate that data + * transfer is postponed. See + * :func:`nghttp2_data_source_read_callback` for details. + */ + NGHTTP2_ERR_DEFERRED = -508, + /** + * Stream ID has reached the maximum value. Therefore no stream ID + * is available. + */ + NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509, + /** + * The stream is already closed; or the stream ID is invalid. + */ + NGHTTP2_ERR_STREAM_CLOSED = -510, + /** + * RST_STREAM has been added to the outbound queue. The stream is + * in closing state. + */ + NGHTTP2_ERR_STREAM_CLOSING = -511, + /** + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent). + */ + NGHTTP2_ERR_STREAM_SHUT_WR = -512, + /** + * The stream ID is invalid. + */ + NGHTTP2_ERR_INVALID_STREAM_ID = -513, + /** + * The state of the stream is not valid (e.g., DATA cannot be sent + * to the stream if response HEADERS has not been sent). + */ + NGHTTP2_ERR_INVALID_STREAM_STATE = -514, + /** + * Another DATA frame has already been deferred. + */ + NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515, + /** + * Starting new stream is not allowed (e.g., GOAWAY has been sent + * and/or received). + */ + NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516, + /** + * GOAWAY has already been sent. + */ + NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517, + /** + * The received frame contains the invalid header block (e.g., There + * are duplicate header names; or the header names are not encoded + * in US-ASCII character set and not lower cased; or the header name + * is zero-length string; or the header value contains multiple + * in-sequence NUL bytes). + */ + NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518, + /** + * Indicates that the context is not suitable to perform the + * requested operation. + */ + NGHTTP2_ERR_INVALID_STATE = -519, + /** + * The user callback function failed due to the temporal error. + */ + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521, + /** + * The length of the frame is invalid, either too large or too small. + */ + NGHTTP2_ERR_FRAME_SIZE_ERROR = -522, + /** + * Header block inflate/deflate error. + */ + NGHTTP2_ERR_HEADER_COMP = -523, + /** + * Flow control error + */ + NGHTTP2_ERR_FLOW_CONTROL = -524, + /** + * Insufficient buffer size given to function. + */ + NGHTTP2_ERR_INSUFF_BUFSIZE = -525, + /** + * Callback was paused by the application + */ + NGHTTP2_ERR_PAUSE = -526, + /** + * There are too many in-flight SETTING frame and no more + * transmission of SETTINGS is allowed. + */ + NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527, + /** + * The server push is disabled. + */ + NGHTTP2_ERR_PUSH_DISABLED = -528, + /** + * DATA or HEADERS frame for a given stream has been already + * submitted and has not been fully processed yet. Application + * should wait for the transmission of the previously submitted + * frame before submitting another. + */ + NGHTTP2_ERR_DATA_EXIST = -529, + /** + * The current session is closing due to a connection error or + * `nghttp2_session_terminate_session()` is called. + */ + NGHTTP2_ERR_SESSION_CLOSING = -530, + /** + * Invalid HTTP header field was received and stream is going to be + * closed. + */ + NGHTTP2_ERR_HTTP_HEADER = -531, + /** + * Violation in HTTP messaging rule. + */ + NGHTTP2_ERR_HTTP_MESSAGING = -532, + /** + * Stream was refused. + */ + NGHTTP2_ERR_REFUSED_STREAM = -533, + /** + * Unexpected internal error, but recovered. + */ + NGHTTP2_ERR_INTERNAL = -534, + /** + * Indicates that a processing was canceled. + */ + NGHTTP2_ERR_CANCEL = -535, + /** + * When a local endpoint expects to receive SETTINGS frame, it + * receives an other type of frame. + */ + NGHTTP2_ERR_SETTINGS_EXPECTED = -536, + /** + * The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is + * under unexpected condition and processing was terminated (e.g., + * out of memory). If application receives this error code, it must + * stop using that :type:`nghttp2_session` object and only allowed + * operation for that object is deallocate it using + * `nghttp2_session_del()`. + */ + NGHTTP2_ERR_FATAL = -900, + /** + * Out of memory. This is a fatal error. + */ + NGHTTP2_ERR_NOMEM = -901, + /** + * The user callback function failed. This is a fatal error. + */ + NGHTTP2_ERR_CALLBACK_FAILURE = -902, + /** + * Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was + * received and further processing is not possible. + */ + NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903, + /** + * Possible flooding by peer was detected in this HTTP/2 session. + * Flooding is measured by how many PING and SETTINGS frames with + * ACK flag set are queued for transmission. These frames are + * response for the peer initiated frames, and peer can cause memory + * exhaustion on server side to send these frames forever and does + * not read network. + */ + NGHTTP2_ERR_FLOODED = -904 +} nghttp2_error; + +/** + * @struct + * + * The object representing single contiguous buffer. + */ +typedef struct { + /** + * The pointer to the buffer. + */ + uint8_t *base; + /** + * The length of the buffer. + */ + size_t len; +} nghttp2_vec; + +struct nghttp2_rcbuf; + +/** + * @struct + * + * The object representing reference counted buffer. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_rcbuf nghttp2_rcbuf; + +/** + * @function + * + * Increments the reference count of |rcbuf| by 1. + */ +NGHTTP2_EXTERN void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf); + +/** + * @function + * + * Decrements the reference count of |rcbuf| by 1. If the reference + * count becomes zero, the object pointed by |rcbuf| will be freed. + * In this case, application must not use |rcbuf| again. + */ +NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf); + +/** + * @function + * + * Returns the underlying buffer managed by |rcbuf|. + */ +NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf); + +/** + * @function + * + * Returns nonzero if the underlying buffer is statically allocated, + * and 0 otherwise. This can be useful for language bindings that wish + * to avoid creating duplicate strings for these buffers. + */ +NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf); + +/** + * @enum + * + * The flags for header field name/value pair. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_NV_FLAG_NONE = 0, + /** + * Indicates that this name/value pair must not be indexed ("Literal + * Header Field never Indexed" representation must be used in HPACK + * encoding). Other implementation calls this bit as "sensitive". + */ + NGHTTP2_NV_FLAG_NO_INDEX = 0x01, + /** + * This flag is set solely by application. If this flag is set, the + * library does not make a copy of header field name. This could + * improve performance. + */ + NGHTTP2_NV_FLAG_NO_COPY_NAME = 0x02, + /** + * This flag is set solely by application. If this flag is set, the + * library does not make a copy of header field value. This could + * improve performance. + */ + NGHTTP2_NV_FLAG_NO_COPY_VALUE = 0x04 +} nghttp2_nv_flag; + +/** + * @struct + * + * The name/value pair, which mainly used to represent header fields. + */ +typedef struct { + /** + * The |name| byte string. If this struct is presented from library + * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is + * guaranteed to be NULL-terminated. For some callbacks + * (:type:`nghttp2_before_frame_send_callback`, + * :type:`nghttp2_on_frame_send_callback`, and + * :type:`nghttp2_on_frame_not_send_callback`), it may not be + * NULL-terminated if header field is passed from application with + * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`). When application + * is constructing this struct, |name| is not required to be + * NULL-terminated. + */ + uint8_t *name; + /** + * The |value| byte string. If this struct is presented from + * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value| + * is guaranteed to be NULL-terminated. For some callbacks + * (:type:`nghttp2_before_frame_send_callback`, + * :type:`nghttp2_on_frame_send_callback`, and + * :type:`nghttp2_on_frame_not_send_callback`), it may not be + * NULL-terminated if header field is passed from application with + * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE`). When + * application is constructing this struct, |value| is not required + * to be NULL-terminated. + */ + uint8_t *value; + /** + * The length of the |name|, excluding terminating NULL. + */ + size_t namelen; + /** + * The length of the |value|, excluding terminating NULL. + */ + size_t valuelen; + /** + * Bitwise OR of one or more of :type:`nghttp2_nv_flag`. + */ + uint8_t flags; +} nghttp2_nv; + +/** + * @enum + * + * The frame types in HTTP/2 specification. + */ +typedef enum { + /** + * The DATA frame. + */ + NGHTTP2_DATA = 0, + /** + * The HEADERS frame. + */ + NGHTTP2_HEADERS = 0x01, + /** + * The PRIORITY frame. + */ + NGHTTP2_PRIORITY = 0x02, + /** + * The RST_STREAM frame. + */ + NGHTTP2_RST_STREAM = 0x03, + /** + * The SETTINGS frame. + */ + NGHTTP2_SETTINGS = 0x04, + /** + * The PUSH_PROMISE frame. + */ + NGHTTP2_PUSH_PROMISE = 0x05, + /** + * The PING frame. + */ + NGHTTP2_PING = 0x06, + /** + * The GOAWAY frame. + */ + NGHTTP2_GOAWAY = 0x07, + /** + * The WINDOW_UPDATE frame. + */ + NGHTTP2_WINDOW_UPDATE = 0x08, + /** + * The CONTINUATION frame. This frame type won't be passed to any + * callbacks because the library processes this frame type and its + * preceding HEADERS/PUSH_PROMISE as a single frame. + */ + NGHTTP2_CONTINUATION = 0x09, + /** + * The ALTSVC frame, which is defined in `RFC 7383 + * `_. + */ + NGHTTP2_ALTSVC = 0x0a, + /** + * The ORIGIN frame, which is defined by `RFC 8336 + * `_. + */ + NGHTTP2_ORIGIN = 0x0c +} nghttp2_frame_type; + +/** + * @enum + * + * The flags for HTTP/2 frames. This enum defines all flags for all + * frames. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_FLAG_NONE = 0, + /** + * The END_STREAM flag. + */ + NGHTTP2_FLAG_END_STREAM = 0x01, + /** + * The END_HEADERS flag. + */ + NGHTTP2_FLAG_END_HEADERS = 0x04, + /** + * The ACK flag. + */ + NGHTTP2_FLAG_ACK = 0x01, + /** + * The PADDED flag. + */ + NGHTTP2_FLAG_PADDED = 0x08, + /** + * The PRIORITY flag. + */ + NGHTTP2_FLAG_PRIORITY = 0x20 +} nghttp2_flag; + +/** + * @enum + * The SETTINGS ID. + */ +typedef enum { + /** + * SETTINGS_HEADER_TABLE_SIZE + */ + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01, + /** + * SETTINGS_ENABLE_PUSH + */ + NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02, + /** + * SETTINGS_MAX_CONCURRENT_STREAMS + */ + NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03, + /** + * SETTINGS_INITIAL_WINDOW_SIZE + */ + NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04, + /** + * SETTINGS_MAX_FRAME_SIZE + */ + NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05, + /** + * SETTINGS_MAX_HEADER_LIST_SIZE + */ + NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06, + /** + * SETTINGS_ENABLE_CONNECT_PROTOCOL + * (`RFC 8441 `_) + */ + NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08 +} nghttp2_settings_id; +/* Note: If we add SETTINGS, update the capacity of + NGHTTP2_INBOUND_NUM_IV as well */ + +/** + * @macro + * + * .. warning:: + * + * Deprecated. The initial max concurrent streams is 0xffffffffu. + * + * Default maximum number of incoming concurrent streams. Use + * `nghttp2_submit_settings()` with + * :enum:`NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS` to change the + * maximum number of incoming concurrent streams. + * + * .. note:: + * + * The maximum number of outgoing concurrent streams is 100 by + * default. + */ +#define NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) + +/** + * @enum + * The status codes for the RST_STREAM and GOAWAY frames. + */ +typedef enum { + /** + * No errors. + */ + NGHTTP2_NO_ERROR = 0x00, + /** + * PROTOCOL_ERROR + */ + NGHTTP2_PROTOCOL_ERROR = 0x01, + /** + * INTERNAL_ERROR + */ + NGHTTP2_INTERNAL_ERROR = 0x02, + /** + * FLOW_CONTROL_ERROR + */ + NGHTTP2_FLOW_CONTROL_ERROR = 0x03, + /** + * SETTINGS_TIMEOUT + */ + NGHTTP2_SETTINGS_TIMEOUT = 0x04, + /** + * STREAM_CLOSED + */ + NGHTTP2_STREAM_CLOSED = 0x05, + /** + * FRAME_SIZE_ERROR + */ + NGHTTP2_FRAME_SIZE_ERROR = 0x06, + /** + * REFUSED_STREAM + */ + NGHTTP2_REFUSED_STREAM = 0x07, + /** + * CANCEL + */ + NGHTTP2_CANCEL = 0x08, + /** + * COMPRESSION_ERROR + */ + NGHTTP2_COMPRESSION_ERROR = 0x09, + /** + * CONNECT_ERROR + */ + NGHTTP2_CONNECT_ERROR = 0x0a, + /** + * ENHANCE_YOUR_CALM + */ + NGHTTP2_ENHANCE_YOUR_CALM = 0x0b, + /** + * INADEQUATE_SECURITY + */ + NGHTTP2_INADEQUATE_SECURITY = 0x0c, + /** + * HTTP_1_1_REQUIRED + */ + NGHTTP2_HTTP_1_1_REQUIRED = 0x0d +} nghttp2_error_code; + +/** + * @struct + * The frame header. + */ +typedef struct { + /** + * The length field of this frame, excluding frame header. + */ + size_t length; + /** + * The stream identifier (aka, stream ID) + */ + int32_t stream_id; + /** + * The type of this frame. See `nghttp2_frame_type`. + */ + uint8_t type; + /** + * The flags. + */ + uint8_t flags; + /** + * Reserved bit in frame header. Currently, this is always set to 0 + * and application should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_frame_hd; + +/** + * @union + * + * This union represents the some kind of data source passed to + * :type:`nghttp2_data_source_read_callback`. + */ +typedef union { + /** + * The integer field, suitable for a file descriptor. + */ + int fd; + /** + * The pointer to an arbitrary object. + */ + void *ptr; +} nghttp2_data_source; + +/** + * @enum + * + * The flags used to set in |data_flags| output parameter in + * :type:`nghttp2_data_source_read_callback`. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_DATA_FLAG_NONE = 0, + /** + * Indicates EOF was sensed. + */ + NGHTTP2_DATA_FLAG_EOF = 0x01, + /** + * Indicates that END_STREAM flag must not be set even if + * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send + * trailer fields with `nghttp2_submit_request()` or + * `nghttp2_submit_response()`. + */ + NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, + /** + * Indicates that application will send complete DATA frame in + * :type:`nghttp2_send_data_callback`. + */ + NGHTTP2_DATA_FLAG_NO_COPY = 0x04 +} nghttp2_data_flag; + +/** + * @functypedef + * + * Callback function invoked when the library wants to read data from + * the |source|. The read data is sent in the stream |stream_id|. + * The implementation of this function must read at most |length| + * bytes of data from |source| (or possibly other places) and store + * them in |buf| and return number of data stored in |buf|. If EOF is + * reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|. + * + * Sometime it is desirable to avoid copying data into |buf| and let + * application to send data directly. To achieve this, set + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` to |*data_flags| (and possibly + * other flags, just like when we do copy), and return the number of + * bytes to send without copying data into |buf|. The library, seeing + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + * :type:`nghttp2_send_data_callback`. The application must send + * complete DATA frame in that callback. + * + * If this callback is set by `nghttp2_submit_request()`, + * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and + * `nghttp2_submit_data()` with flag parameter + * :enum:`NGHTTP2_FLAG_END_STREAM` set, and + * :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA + * frame will have END_STREAM flag set. Usually, this is expected + * behaviour and all are fine. One exception is send trailer fields. + * You cannot send trailer fields after sending frame with END_STREAM + * set. To avoid this problem, one can set + * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with + * :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set + * END_STREAM in DATA frame. Then application can use + * `nghttp2_submit_trailer()` to send trailer fields. + * `nghttp2_submit_trailer()` can be called inside this callback. + * + * If the application wants to postpone DATA frames (e.g., + * asynchronous I/O, or reading data blocks for long time), it is + * achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading + * any data in this invocation. The library removes DATA frame from + * the outgoing queue temporarily. To move back deferred DATA frame + * to outgoing queue, call `nghttp2_session_resume_data()`. + * + * By default, |length| is limited to 16KiB at maximum. If peer + * allows larger frames, application can enlarge transmission buffer + * size. See :type:`nghttp2_data_source_read_length_callback` for + * more details. + * + * If the application just wants to return from + * `nghttp2_session_send()` or `nghttp2_session_mem_send()` without + * sending anything, return :enum:`NGHTTP2_ERR_PAUSE`. + * + * In case of error, there are 2 choices. Returning + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream + * by issuing RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Returning + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + * failure. + */ +typedef ssize_t (*nghttp2_data_source_read_callback)( + nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, + uint32_t *data_flags, nghttp2_data_source *source, void *user_data); + +/** + * @struct + * + * This struct represents the data source and the way to read a chunk + * of data from it. + */ +typedef struct { + /** + * The data source. + */ + nghttp2_data_source source; + /** + * The callback function to read a chunk of data from the |source|. + */ + nghttp2_data_source_read_callback read_callback; +} nghttp2_data_provider; + +/** + * @struct + * + * The DATA frame. The received data is delivered via + * :type:`nghttp2_on_data_chunk_recv_callback`. + */ +typedef struct { + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; +} nghttp2_data; + +/** + * @enum + * + * The category of HEADERS, which indicates the role of the frame. In + * HTTP/2 spec, request, response, push response and other arbitrary + * headers (e.g., trailer fields) are all called just HEADERS. To + * give the application the role of incoming HEADERS frame, we define + * several categories. + */ +typedef enum { + /** + * The HEADERS frame is opening new stream, which is analogous to + * SYN_STREAM in SPDY. + */ + NGHTTP2_HCAT_REQUEST = 0, + /** + * The HEADERS frame is the first response headers, which is + * analogous to SYN_REPLY in SPDY. + */ + NGHTTP2_HCAT_RESPONSE = 1, + /** + * The HEADERS frame is the first headers sent against reserved + * stream. + */ + NGHTTP2_HCAT_PUSH_RESPONSE = 2, + /** + * The HEADERS frame which does not apply for the above categories, + * which is analogous to HEADERS in SPDY. If non-final response + * (e.g., status 1xx) is used, final response HEADERS frame will be + * categorized here. + */ + NGHTTP2_HCAT_HEADERS = 3 +} nghttp2_headers_category; + +/** + * @struct + * + * The structure to specify stream dependency. + */ +typedef struct { + /** + * The stream ID of the stream to depend on. Specifying 0 makes + * stream not depend any other stream. + */ + int32_t stream_id; + /** + * The weight of this dependency. + */ + int32_t weight; + /** + * nonzero means exclusive dependency + */ + uint8_t exclusive; +} nghttp2_priority_spec; + +/** + * @struct + * + * The HEADERS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; + /** + * The priority specification + */ + nghttp2_priority_spec pri_spec; + /** + * The name/value pairs. + */ + nghttp2_nv *nva; + /** + * The number of name/value pairs in |nva|. + */ + size_t nvlen; + /** + * The category of this HEADERS frame. + */ + nghttp2_headers_category cat; +} nghttp2_headers; + +/** + * @struct + * + * The PRIORITY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The priority specification. + */ + nghttp2_priority_spec pri_spec; +} nghttp2_priority; + +/** + * @struct + * + * The RST_STREAM frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; +} nghttp2_rst_stream; + +/** + * @struct + * + * The SETTINGS ID/Value pair. It has the following members: + */ +typedef struct { + /** + * The SETTINGS ID. See :type:`nghttp2_settings_id`. + */ + int32_t settings_id; + /** + * The value of this entry. + */ + uint32_t value; +} nghttp2_settings_entry; + +/** + * @struct + * + * The SETTINGS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The number of SETTINGS ID/Value pairs in |iv|. + */ + size_t niv; + /** + * The pointer to the array of SETTINGS ID/Value pair. + */ + nghttp2_settings_entry *iv; +} nghttp2_settings; + +/** + * @struct + * + * The PUSH_PROMISE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; + /** + * The name/value pairs. + */ + nghttp2_nv *nva; + /** + * The number of name/value pairs in |nva|. + */ + size_t nvlen; + /** + * The promised stream ID + */ + int32_t promised_stream_id; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_push_promise; + +/** + * @struct + * + * The PING frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The opaque data + */ + uint8_t opaque_data[8]; +} nghttp2_ping; + +/** + * @struct + * + * The GOAWAY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The last stream stream ID. + */ + int32_t last_stream_id; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; + /** + * The additional debug data + */ + uint8_t *opaque_data; + /** + * The length of |opaque_data| member. + */ + size_t opaque_data_len; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_goaway; + +/** + * @struct + * + * The WINDOW_UPDATE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The window size increment. + */ + int32_t window_size_increment; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_window_update; + +/** + * @struct + * + * The extension frame. It has following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The pointer to extension payload. The exact pointer type is + * determined by hd.type. + * + * Currently, no extension is supported. This is a place holder for + * the future extensions. + */ + void *payload; +} nghttp2_extension; + +/** + * @union + * + * This union includes all frames to pass them to various function + * calls as nghttp2_frame type. The CONTINUATION frame is omitted + * from here because the library deals with it internally. + */ +typedef union { + /** + * The frame header, which is convenient to inspect frame header. + */ + nghttp2_frame_hd hd; + /** + * The DATA frame. + */ + nghttp2_data data; + /** + * The HEADERS frame. + */ + nghttp2_headers headers; + /** + * The PRIORITY frame. + */ + nghttp2_priority priority; + /** + * The RST_STREAM frame. + */ + nghttp2_rst_stream rst_stream; + /** + * The SETTINGS frame. + */ + nghttp2_settings settings; + /** + * The PUSH_PROMISE frame. + */ + nghttp2_push_promise push_promise; + /** + * The PING frame. + */ + nghttp2_ping ping; + /** + * The GOAWAY frame. + */ + nghttp2_goaway goaway; + /** + * The WINDOW_UPDATE frame. + */ + nghttp2_window_update window_update; + /** + * The extension frame. + */ + nghttp2_extension ext; +} nghttp2_frame; + +/** + * @functypedef + * + * Callback function invoked when |session| wants to send data to the + * remote peer. The implementation of this function must send at most + * |length| bytes of data stored in |data|. The |flags| is currently + * not used and always 0. It must return the number of bytes sent if + * it succeeds. If it cannot send any single byte without blocking, + * it must return :enum:`NGHTTP2_ERR_WOULDBLOCK`. For other errors, + * it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_send()` to send data to the remote endpoint. If + * the application uses solely `nghttp2_session_mem_send()` instead, + * this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_send_callback()`. + * + * .. note:: + * + * The |length| may be very small. If that is the case, and + * application disables Nagle algorithm (``TCP_NODELAY``), then just + * writing |data| to the network stack leads to very small packet, + * and it is very inefficient. An application should be responsible + * to buffer up small chunks of data as necessary to avoid this + * situation. + */ +typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, + const uint8_t *data, size_t length, + int flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is + * used in :type:`nghttp2_data_source_read_callback` to send complete + * DATA frame. + * + * The |frame| is a DATA frame to send. The |framehd| is the + * serialized frame header (9 bytes). The |length| is the length of + * application data to send (this does not include padding). The + * |source| is the same pointer passed to + * :type:`nghttp2_data_source_read_callback`. + * + * The application first must send frame header |framehd| of length 9 + * bytes. If ``frame->data.padlen > 0``, send 1 byte of value + * ``frame->data.padlen - 1``. Then send exactly |length| bytes of + * application data. Finally, if ``frame->data.padlen > 1``, send + * ``frame->data.padlen - 1`` bytes of zero as padding. + * + * The application has to send complete DATA frame in this callback. + * If all data were written successfully, return 0. + * + * If it cannot send any data at all, just return + * :enum:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback + * with the same parameters later (It is recommended to send complete + * DATA frame at once in this function to deal with error; if partial + * frame data has already sent, it is impossible to send another data + * in that state, and all we can do is tear down connection). When + * data is fully processed, but application wants to make + * `nghttp2_session_mem_send()` or `nghttp2_session_send()` return + * immediately without processing next frames, return + * :enum:`NGHTTP2_ERR_PAUSE`. If application decided to reset this + * stream, return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then + * the library will send RST_STREAM with INTERNAL_ERROR as error code. + * The application can also return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, which will result in + * connection closure. Returning any other value is treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. + */ +typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, + nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when |session| wants to receive data from + * the remote peer. The implementation of this function must read at + * most |length| bytes of data and store it in |buf|. The |flags| is + * currently not used and always 0. It must return the number of + * bytes written in |buf| if it succeeds. If it cannot read any + * single byte without blocking, it must return + * :enum:`NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF before it reads any + * single byte, it must return :enum:`NGHTTP2_ERR_EOF`. For other + * errors, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * Returning 0 is treated as :enum:`NGHTTP2_ERR_WOULDBLOCK`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_recv()` to receive data from the remote endpoint. + * If the application uses solely `nghttp2_session_mem_recv()` + * instead, this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_recv_callback()`. + */ +typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, + size_t length, int flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. The header name/value pairs are emitted via + * :type:`nghttp2_on_header_callback`. + * + * For HEADERS, PUSH_PROMISE and DATA frames, this callback may be + * called after stream is closed (see + * :type:`nghttp2_on_stream_close_callback`). The application should + * check that stream is still alive using its own stream management or + * :func:`nghttp2_session_get_stream_user_data()`. + * + * Only HEADERS and DATA frame can signal the end of incoming data. + * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the + * |frame| is the last frame from the remote peer in this stream. + * + * This callback won't be called for CONTINUATION frames. + * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + * received. The error is indicated by the |lib_error_code|, which is + * one of the values defined in :type:`nghttp2_error`. When this + * callback function is invoked, the library automatically submits + * either RST_STREAM or GOAWAY frame. The |user_data| pointer is the + * third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_invalid_frame_recv_callback)( + nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a chunk of data in DATA frame is + * received. The |stream_id| is the stream ID this DATA frame belongs + * to. The |flags| is the flags of DATA frame which this data chunk + * is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not + * necessarily mean this chunk of data is the last one in the stream. + * You should use :type:`nghttp2_on_frame_recv_callback` to know all + * data frames are received. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * If the application uses `nghttp2_session_mem_recv()`, it can return + * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + * return without processing further input bytes. The memory by + * pointed by the |data| is retained until + * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + * The application must retain the input bytes which was used to + * produce the |data| parameter, because it may refer to the memory + * region included in the input bytes. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error, and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. + */ +typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked just before the non-DATA frame |frame| is + * sent. The |user_data| pointer is the third argument passed in to + * the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * It can also return :enum:`NGHTTP2_ERR_CANCEL` to cancel the + * transmission of the given frame. + * + * If there is a fatal error while executing this callback, the + * implementation should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, + * which makes `nghttp2_session_send()` and + * `nghttp2_session_mem_send()` functions immediately return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * If the other value is returned, it is treated as if + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. But the + * implementation should not rely on this since the library may define + * new return value to extend its capability. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_before_frame_send_callback()`. + */ +typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the frame |frame| is sent. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_send_callback()`. + */ +typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the non-DATA frame |frame| is not + * sent because of the error. The error is indicated by the + * |lib_error_code|, which is one of the values defined in + * :type:`nghttp2_error`. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * `nghttp2_session_get_stream_user_data()` can be used to get + * associated data. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. + */ +typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the stream |stream_id| is closed. + * The reason of closure is indicated by the |error_code|. The + * |error_code| is usually one of :enum:`nghttp2_error_code`, but that + * is not guaranteed. The stream_user_data, which was specified in + * `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still + * available in this function. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * This function is also called for a stream in reserved state. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, + * `nghttp2_session_send()`, and `nghttp2_session_mem_send()` + * functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_stream_close_callback()`. + */ +typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. Each header name/value pair + * will be emitted by :type:`nghttp2_on_header_callback`. + * + * The ``frame->hd.flags`` may not have + * :enum:`NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one + * or more CONTINUATION frames are involved. But the application does + * not need to care about that because the header name/value pairs are + * emitted transparently regardless of CONTINUATION frames. + * + * The server applications probably create an object to store + * information about new stream if ``frame->hd.type == + * NGHTTP2_HEADERS`` and ``frame->headers.cat == + * NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side, + * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` + * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing + * trailer fields and never get PUSH_PROMISE in this callback. + * + * For the client applications, ``frame->hd.type`` is either + * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of + * ``NGHTTP2_HEADERS``, ``frame->headers.cat == + * NGHTTP2_HCAT_RESPONSE`` means that it is the first response + * headers, but it may be non-final response which is indicated by 1xx + * status code. In this case, there may be zero or more HEADERS frame + * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has + * non-final response code and finally client gets exactly one HEADERS + * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` + * containing final response headers (non-1xx status code). The + * trailer fields also has ``frame->headers.cat == + * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. + * + * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + * the stream (promised stream if frame is PUSH_PROMISE) by issuing + * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + * ``frame->push_promise.promised_stream_id`` as stream_id parameter + * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to + * reset the stream (promised stream if frame is PUSH_PROMISE). For + * critical errors, it must return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is + * returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * is returned. If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_mem_recv()` function will immediately return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_headers_callback()`. + */ +typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| of length |namelen| is header name. + * The |value| of length |valuelen| is header value. The |flags| is + * bitwise OR of one or more of :type:`nghttp2_nv_flag`. + * + * If :enum:`NGHTTP2_NV_FLAG_NO_INDEX` is set in |flags|, the receiver + * must not index this name/value pair when forwarding it to the next + * hop. More specifically, "Literal Header Field never Indexed" + * representation must be used in HPACK encoding. + * + * When this callback is invoked, ``frame->hd.type`` is either + * :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`. After all + * header name/value pairs are processed with this callback, and no + * error has been detected, :type:`nghttp2_on_frame_recv_callback` + * will be invoked. If there is an error in decompression, + * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be + * invoked. + * + * Both |name| and |value| are guaranteed to be NULL-terminated. The + * |namelen| and |valuelen| do not include terminal NULL. If + * `nghttp2_option_set_no_http_messaging()` is used with nonzero + * value, NULL character may be included in |name| or |value| before + * terminating NULL. + * + * Please note that unless `nghttp2_option_set_no_http_messaging()` is + * used, nghttp2 library does perform validation against the |name| + * and the |value| using `nghttp2_check_header_name()` and + * `nghttp2_check_header_value()`. In addition to this, nghttp2 + * performs validation based on HTTP Messaging rule, which is briefly + * explained in :ref:`http-messaging` section. + * + * If the application uses `nghttp2_session_mem_recv()`, it can return + * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + * return without processing further input bytes. The memory pointed + * by |frame|, |name| and |value| parameters are retained until + * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + * The application must retain the input bytes which was used to + * produce these parameters, because it may refer to the memory region + * included in the input bytes. + * + * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + * the stream (promised stream if frame is PUSH_PROMISE) by issuing + * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + * ``frame->push_promise.promised_stream_id`` as stream_id parameter + * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It may return :enum:`NGHTTP2_ERR_PAUSE` or + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For other critical + * failures, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + * the other nonzero value is returned, it is treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_header_callback()`. + * + * .. warning:: + * + * Application should properly limit the total buffer size to store + * incoming header fields. Without it, peer may send large number + * of header fields or large header fields to cause out of memory in + * local endpoint. Due to how HPACK works, peer can do this + * effectively without using much memory on their own. + */ +typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| is header name. The |value| is header + * value. The |flags| is bitwise OR of one or more of + * :type:`nghttp2_nv_flag`. + * + * This callback behaves like :type:`nghttp2_on_header_callback`, + * except that |name| and |value| are stored in reference counted + * buffer. If application wishes to keep these references without + * copying them, use `nghttp2_rcbuf_incref()` to increment their + * reference count. It is the application's responsibility to call + * `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so + * as not to leak memory. If the |session| is created by + * `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`, + * the function to free memory is the one belongs to the mem + * parameter. As long as this free function alives, |name| and + * |value| can live after |session| was destroyed. + */ +typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session, + const nghttp2_frame *frame, + nghttp2_rcbuf *name, + nghttp2_rcbuf *value, uint8_t flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback`. The difference is that this + * callback is only invoked when a invalid header name/value pair is + * received which is treated as stream error if this callback is not + * set. Only invalid regular header field are passed to this + * callback. In other words, invalid pseudo header field is not + * passed to this callback. Also header fields which includes upper + * cased latter are also treated as error without passing them to this + * callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see + * `nghttp2_option_set_no_http_messaging()`). + * + * With this callback, application inspects the incoming invalid + * field, and it also can reset stream from this callback by returning + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By default, the + * error code is :enum:`NGHTTP2_PROTOCOL_ERROR`. To change the error + * code, call `nghttp2_submit_rst_stream()` with the error code of + * choice in addition to returning + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * + * If 0 is returned, the header field is ignored, and the stream is + * not reset. + */ +typedef int (*nghttp2_on_invalid_header_callback)( + nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback2`. The difference is that this + * callback is only invoked when a invalid header name/value pair is + * received which is silently ignored if this callback is not set. + * Only invalid regular header field are passed to this callback. In + * other words, invalid pseudo header field is not passed to this + * callback. Also header fields which includes upper cased latter are + * also treated as error without passing them to this callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see + * `nghttp2_option_set_no_http_messaging()`). + * + * With this callback, application inspects the incoming invalid + * field, and it also can reset stream from this callback by returning + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By default, the + * error code is :enum:`NGHTTP2_INTERNAL_ERROR`. To change the error + * code, call `nghttp2_submit_rst_stream()` with the error code of + * choice in addition to returning + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + */ +typedef int (*nghttp2_on_invalid_header_callback2)( + nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, + nghttp2_rcbuf *value, uint8_t flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the + * |frame|. The application must choose the total length of payload + * including padded bytes in range [frame->hd.length, max_payloadlen], + * inclusive. Choosing number not in this range will be treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning + * ``frame->hd.length`` means no padding is added. Returning + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_select_padding_callback()`. + */ +typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library wants to get max length of + * data to send data to the remote peer. The implementation of this + * function should return a value in the following range. [1, + * min(|session_remote_window_size|, |stream_remote_window_size|, + * |remote_max_frame_size|)]. If a value greater than this range is + * returned than the max allow value will be used. Returning a value + * smaller than this range is treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. The |frame_type| is provided + * for future extensibility and identifies the type of frame (see + * :type:`nghttp2_frame_type`) for which to get the length for. + * Currently supported frame types are: :enum:`NGHTTP2_DATA`. + * + * This callback can be used to control the length in bytes for which + * :type:`nghttp2_data_source_read_callback` is allowed to send to the + * remote endpoint. This callback is optional. Returning + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + * failure. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_data_source_read_length_callback()`. + */ +typedef ssize_t (*nghttp2_data_source_read_length_callback)( + nghttp2_session *session, uint8_t frame_type, int32_t stream_id, + int32_t session_remote_window_size, int32_t stream_remote_window_size, + uint32_t remote_max_frame_size, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a frame header is received. The + * |hd| points to received frame header. + * + * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will + * also be called when frame header of CONTINUATION frame is received. + * + * If both :type:`nghttp2_on_begin_frame_callback` and + * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or + * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` + * will be called first. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_frame_callback()`. + */ +typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, + const nghttp2_frame_hd *hd, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when chunk of extension frame payload is + * received. The |hd| points to frame header. The received + * chunk is |data| of length |len|. + * + * The implementation of this function must return 0 if it succeeds. + * + * To abort processing this extension frame, return + * :enum:`NGHTTP2_ERR_CANCEL`. + * + * If fatal error occurred, application should return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the + * other values are returned, currently they are treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp2_on_extension_chunk_recv_callback)( + nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library asks the application to + * unpack extension payload from its wire format. The extension + * payload has been passed to the application using + * :type:`nghttp2_on_extension_chunk_recv_callback`. The frame header + * is already unpacked by the library and provided as |hd|. + * + * To receive extension frames, the application must tell desired + * extension frame type to the library using + * `nghttp2_option_set_user_recv_extension_type()`. + * + * The implementation of this function may store the pointer to the + * created object as a result of unpacking in |*payload|, and returns + * 0. The pointer stored in |*payload| is opaque to the library, and + * the library does not own its pointer. |*payload| is initialized as + * ``NULL``. The |*payload| is available as ``frame->ext.payload`` in + * :type:`nghttp2_on_frame_recv_callback`. Therefore if application + * can free that memory inside :type:`nghttp2_on_frame_recv_callback` + * callback. Of course, application has a liberty not ot use + * |*payload|, and do its own mechanism to process extension frames. + * + * To abort processing this extension frame, return + * :enum:`NGHTTP2_ERR_CANCEL`. + * + * If fatal error occurred, application should return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the + * other values are returned, currently they are treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session, + void **payload, + const nghttp2_frame_hd *hd, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library asks the application to pack + * extension payload in its wire format. The frame header will be + * packed by library. Application must pack payload only. + * ``frame->ext.payload`` is the object passed to + * `nghttp2_submit_extension()` as payload parameter. Application + * must pack extension payload to the |buf| of its capacity |len| + * bytes. The |len| is at least 16KiB. + * + * The implementation of this function should return the number of + * bytes written into |buf| when it succeeds. + * + * To abort processing this extension frame, return + * :enum:`NGHTTP2_ERR_CANCEL`, and + * :type:`nghttp2_on_frame_not_send_callback` will be invoked. + * + * If fatal error occurred, application should return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the + * other values are returned, currently they are treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the return value is + * strictly larger than |len|, it is treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session, + uint8_t *buf, size_t len, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library provides the error message + * intended for human consumption. This callback is solely for + * debugging purpose. The |msg| is typically NULL-terminated string + * of length |len|. |len| does not include the sentinel NULL + * character. + * + * This function is deprecated. The new application should use + * :type:`nghttp2_error_callback2`. + * + * The format of error message may change between nghttp2 library + * versions. The application should not depend on the particular + * format. + * + * Normally, application should return 0 from this callback. If fatal + * error occurred while doing something in this callback, application + * should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * library will return immediately with return value + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if nonzero value + * is returned from this callback, they are treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not + * rely on this details. + */ +typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library provides the error code, and + * message. This callback is solely for debugging purpose. + * |lib_error_code| is one of error code defined in + * :enum:`nghttp2_error`. The |msg| is typically NULL-terminated + * string of length |len|, and intended for human consumption. |len| + * does not include the sentinel NULL character. + * + * The format of error message may change between nghttp2 library + * versions. The application should not depend on the particular + * format. + * + * Normally, application should return 0 from this callback. If fatal + * error occurred while doing something in this callback, application + * should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * library will return immediately with return value + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if nonzero value + * is returned from this callback, they are treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not + * rely on this details. + */ +typedef int (*nghttp2_error_callback2)(nghttp2_session *session, + int lib_error_code, const char *msg, + size_t len, void *user_data); + +struct nghttp2_session_callbacks; + +/** + * @struct + * + * Callback functions for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; + +/** + * @function + * + * Initializes |*callbacks_ptr| with NULL values. + * + * The initialized object can be used when initializing multiple + * :type:`nghttp2_session` objects. + * + * When the application finished using this object, it can use + * `nghttp2_session_callbacks_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); + +/** + * @function + * + * Frees any resources allocated for |callbacks|. If |callbacks| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); + +/** + * @function + * + * Sets callback function invoked when a session wants to send data to + * the remote peer. This callback is not necessary if the application + * uses solely `nghttp2_session_mem_send()` to serialize data to + * transmit. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); + +/** + * @function + * + * Sets callback function invoked when the a session wants to receive + * data from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + * received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked when a chunk of data in DATA frame + * is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); + +/** + * @function + * + * Sets callback function invoked before a non-DATA frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked after a frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked when a non-DATA frame is not sent + * because of an error. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback); + +/** + * @function + * + * Sets callback function invoked when the stream is closed. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback); + +/** + * @function + * + * Sets callback function invoked when the reception of header block + * in HEADERS or PUSH_PROMISE is started. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. If both + * `nghttp2_session_callbacks_set_on_header_callback()` and + * `nghttp2_session_callbacks_set_on_header_callback2()` are used to + * set callbacks, the latter has the precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback2 on_header_callback2); + +/** + * @function + * + * Sets callback function invoked when a invalid header name/value + * pair is received. If both + * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and + * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are + * used to set callbacks, the latter takes the precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback on_invalid_header_callback); + +/** + * @function + * + * Sets callback function invoked when a invalid header name/value + * pair is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback2 on_invalid_header_callback2); + +/** + * @function + * + * Sets callback function invoked when the library asks application + * how many padding bytes are required for the transmission of the + * given frame. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback); + +/** + * @function + * + * Sets callback function determine the length allowed in + * :type:`nghttp2_data_source_read_callback`. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback); + +/** + * @function + * + * Sets callback function invoked when a frame header is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback); + +/** + * @function + * + * Sets callback function invoked when + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is used in + * :type:`nghttp2_data_source_read_callback` to avoid data copy. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback); + +/** + * @function + * + * Sets callback function invoked when the library asks the + * application to pack extension frame payload in wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_pack_extension_callback pack_extension_callback); + +/** + * @function + * + * Sets callback function invoked when the library asks the + * application to unpack extension frame payload from wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_unpack_extension_callback unpack_extension_callback); + +/** + * @function + * + * Sets callback function invoked when chunk of extension frame + * payload is received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback); + +/** + * @function + * + * Sets callback function invoked when library tells error message to + * the application. + * + * This function is deprecated. The new application should use + * `nghttp2_session_callbacks_set_error_callback2()`. + * + * If both :type:`nghttp2_error_callback` and + * :type:`nghttp2_error_callback2` are set, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback( + nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback); + +/** + * @function + * + * Sets callback function invoked when library tells error code, and + * message to the application. + * + * If both :type:`nghttp2_error_callback` and + * :type:`nghttp2_error_callback2` are set, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2( + nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2); + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * Custom memory allocator functions and user defined pointer. The + * |mem_user_data| member is passed to each allocator function. This + * can be used, for example, to achieve per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void session_new() { + * nghttp2_session *session; + * nghttp2_session_callbacks *callbacks; + * nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * + * nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); + * + * ... + * } + */ +typedef struct { + /** + * An arbitrary user supplied data. This is passed to each + * allocator function. + */ + void *mem_user_data; + /** + * Custom allocator function to replace malloc(). + */ + nghttp2_malloc malloc; + /** + * Custom allocator function to replace free(). + */ + nghttp2_free free; + /** + * Custom allocator function to replace calloc(). + */ + nghttp2_calloc calloc; + /** + * Custom allocator function to replace realloc(). + */ + nghttp2_realloc realloc; +} nghttp2_mem; + +struct nghttp2_option; + +/** + * @struct + * + * Configuration options for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_option nghttp2_option; + +/** + * @function + * + * Initializes |*option_ptr| with default values. + * + * When the application finished using this object, it can use + * `nghttp2_option_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); + +/** + * @function + * + * Frees any resources allocated for |option|. If |option| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); + +/** + * @function + * + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * `nghttp2_session_consume()` to indicate the consumed amount of + * data. Don't use `nghttp2_submit_window_update()` for this purpose. + * By default, this option is set to zero. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); + +/** + * @function + * + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without + * specifying this option, the maximum number of outgoing concurrent + * streams is initially limited to 100 to avoid issues when the local + * endpoint submits lots of requests before receiving initial SETTINGS + * frame from the remote endpoint, since sending them at once to the + * remote endpoint could lead to rejection of some of the requests. + * This value will be overwritten when the local endpoint receives + * initial SETTINGS frame from the remote endpoint, either to the + * value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the + * default value (unlimited) if none was advertised. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * By default, nghttp2 library, if configured as server, requires + * first 24 bytes of client magic byte string (MAGIC). In most cases, + * this will simplify the implementation of server. But sometimes + * server may want to detect the application protocol based on first + * few bytes on clear text communication. + * + * If this option is used with nonzero |val|, nghttp2 library does not + * handle MAGIC. It still checks following SETTINGS frame. This + * means that applications should deal with MAGIC by themselves. + * + * If this option is not used or used with zero value, if MAGIC does + * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` + * and `nghttp2_session_mem_recv()` will return error + * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val); + +/** + * @function + * + * By default, nghttp2 library enforces subset of HTTP Messaging rules + * described in `HTTP/2 specification, section 8 + * `_. See + * :ref:`http-messaging` section for details. For those applications + * who use nghttp2 library as non-HTTP use, give nonzero to |val| to + * disable this enforcement. Please note that disabling this feature + * does not change the fundamental client and server model of HTTP. + * That is, even if the validation is disabled, only client can send + * requests. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, + int val); + +/** + * @function + * + * RFC 7540 does not enforce any limit on the number of incoming + * reserved streams (in RFC 7540 terms, streams in reserved (remote) + * state). This only affects client side, since only server can push + * streams. Malicious server can push arbitrary number of streams, + * and make client's memory exhausted. This option can set the + * maximum number of such incoming streams to avoid possible memory + * exhaustion. If this option is set, and pushed streams are + * automatically closed on reception, without calling user provided + * callback, if they exceed the given limit. The default value is + * 200. If session is configured as server side, this option has no + * effect. Server can control the number of streams to push. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * Sets extension frame type the application is willing to handle with + * user defined callbacks (see + * :type:`nghttp2_on_extension_chunk_recv_callback` and + * :type:`nghttp2_unpack_extension_callback`). The |type| is + * extension frame type, and must be strictly greater than 0x9. + * Otherwise, this function does nothing. The application can call + * this function multiple times to set more than one frame type to + * receive. The application does not have to call this function if it + * just sends extension frames. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, + uint8_t type); + +/** + * @function + * + * Sets extension frame type the application is willing to receive + * using builtin handler. The |type| is the extension frame type to + * receive, and must be strictly greater than 0x9. Otherwise, this + * function does nothing. The application can call this function + * multiple times to set more than one frame type to receive. The + * application does not have to call this function if it just sends + * extension frames. + * + * If same frame type is passed to both + * `nghttp2_option_set_builtin_recv_extension_type()` and + * `nghttp2_option_set_user_recv_extension_type()`, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, + uint8_t type); + +/** + * @function + * + * This option prevents the library from sending PING frame with ACK + * flag set automatically when PING frame without ACK flag set is + * received. If this option is set to nonzero, the library won't send + * PING frame with ACK flag set in the response for incoming PING + * frame. The application can send PING frame with ACK flag set using + * `nghttp2_submit_ping()` with :enum:`NGHTTP2_FLAG_ACK` as flags + * parameter. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, + int val); + +/** + * @function + * + * This option sets the maximum length of header block (a set of + * header fields per one HEADERS frame) to send. The length of a + * given set of header fields is calculated using + * `nghttp2_hd_deflate_bound()`. The default value is 64KiB. If + * application attempts to send header fields larger than this limit, + * the transmission of the frame fails with error code + * :enum:`NGHTTP2_ERR_FRAME_SIZE_ERROR`. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option sets the maximum dynamic table size for deflating + * header fields. The default value is 4KiB. In HTTP/2, receiver of + * deflated header block can specify maximum dynamic table size. The + * actual maximum size is the minimum of the size receiver specified + * and this option value. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option prevents the library from retaining closed streams to + * maintain the priority tree. If this option is set to nonzero, + * applications can discard closed stream completely to save memory. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, + int val); + +/** + * @function + * + * This function sets the maximum number of outgoing SETTINGS ACK and + * PING ACK frames retained in :type:`nghttp2_session` object. If + * more than those frames are retained, the peer is considered to be + * misbehaving and session will be closed. The default value is 1000. + */ +NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, + size_t val); + +/** + * @function + * + * Initializes |*session_ptr| for client use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Initializes |*session_ptr| for server use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Like `nghttp2_session_client_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_server_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_client_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_client_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Like `nghttp2_session_server_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_server_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Frees any resources allocated for |session|. If |session| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); + +/** + * @function + * + * Sends pending frames to the remote peer. + * + * This function retrieves the highest prioritized frame from the + * outbound queue and sends it to the remote peer. It does this as + * many as possible until the user callback + * :type:`nghttp2_send_callback` returns + * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty. + * This function calls several callback functions which are passed + * when initializing the |session|. Here is the simple time chart + * which tells when each callback is invoked: + * + * 1. Get the next frame to send from outbound queue. + * + * 2. Prepare transmission of the frame. + * + * 3. If the control frame cannot be sent because some preconditions + * are not met (e.g., request HEADERS cannot be sent after GOAWAY), + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 4. If the frame is HEADERS, PUSH_PROMISE or DATA, + * :type:`nghttp2_select_padding_callback` is invoked. + * + * 5. If the frame is request HEADERS, the stream is opened here. + * + * 6. :type:`nghttp2_before_frame_send_callback` is invoked. + * + * 7. If :enum:`NGHTTP2_ERR_CANCEL` is returned from + * :type:`nghttp2_before_frame_send_callback`, the current frame + * transmission is canceled, and + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 8. :type:`nghttp2_send_callback` is invoked one or more times to + * send the frame. + * + * 9. :type:`nghttp2_on_frame_send_callback` is invoked. + * + * 10. If the transmission of the frame triggers closure of the + * stream, the stream is closed and + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + */ +NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); + +/** + * @function + * + * Returns the serialized data to send. + * + * This function behaves like `nghttp2_session_send()` except that it + * does not use :type:`nghttp2_send_callback` to transmit data. + * Instead, it assigns the pointer to the serialized data to the + * |*data_ptr| and returns its length. The other callbacks are called + * in the same way as they are in `nghttp2_session_send()`. + * + * If no data is available to send, this function returns 0. + * + * This function may not return all serialized data in one invocation. + * To get all data, call this function repeatedly until it returns 0 + * or one of negative error codes. + * + * The assigned |*data_ptr| is valid until the next call of + * `nghttp2_session_mem_send()` or `nghttp2_session_send()`. + * + * The caller must send all data before sending the next chunk of + * data. + * + * This function returns the length of the data pointed by the + * |*data_ptr| if it succeeds, or one of the following negative error + * codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * + * .. note:: + * + * This function may produce very small byte string. If that is the + * case, and application disables Nagle algorithm (``TCP_NODELAY``), + * then writing this small chunk leads to very small packet, and it + * is very inefficient. An application should be responsible to + * buffer up small chunks of data as necessary to avoid this + * situation. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr); + +/** + * @function + * + * Receives frames from the remote peer. + * + * This function receives as many frames as possible until the user + * callback :type:`nghttp2_recv_callback` returns + * :enum:`NGHTTP2_ERR_WOULDBLOCK`. This function calls several + * callback functions which are passed when initializing the + * |session|. Here is the simple time chart which tells when each + * callback is invoked: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive frame header. + * + * 2. When frame header is received, + * :type:`nghttp2_on_begin_frame_callback` is invoked. + * + * 3. If the frame is DATA frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked to receive DATA + * payload. For each chunk of data, + * :type:`nghttp2_on_data_chunk_recv_callback` is invoked. + * + * 2. If one DATA frame is completely received, + * :type:`nghttp2_on_frame_recv_callback` is invoked. If the + * reception of the frame triggers the closure of the stream, + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 4. If the frame is the control frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive whole frame. + * + * 2. If the received frame is valid, then following actions are + * taken. If the frame is either HEADERS or PUSH_PROMISE, + * :type:`nghttp2_on_begin_headers_callback` is invoked. Then + * :type:`nghttp2_on_header_callback` is invoked for each header + * name/value pair. For invalid header field, + * :type:`nghttp2_on_invalid_header_callback` is called. After + * all name/value pairs are emitted successfully, + * :type:`nghttp2_on_frame_recv_callback` is invoked. For other + * frames, :type:`nghttp2_on_frame_recv_callback` is invoked. + * If the reception of the frame triggers the closure of the + * stream, :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 3. If the received frame is unpacked but is interpreted as + * invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is + * invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_EOF` + * The remote peer did shutdown on the connection. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + * :enum:`NGHTTP2_ERR_FLOODED` + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); + +/** + * @function + * + * Processes data |in| as an input from the remote endpoint. The + * |inlen| indicates the number of bytes in the |in|. + * + * This function behaves like `nghttp2_session_recv()` except that it + * does not use :type:`nghttp2_recv_callback` to receive data; the + * |in| is the only data for the invocation of this function. If all + * bytes are processed, this function returns. The other callbacks + * are called in the same way as they are in `nghttp2_session_recv()`. + * + * In the current implementation, this function always tries to + * processes all input data unless either an error occurs or + * :enum:`NGHTTP2_ERR_PAUSE` is returned from + * :type:`nghttp2_on_header_callback` or + * :type:`nghttp2_on_data_chunk_recv_callback`. If + * :enum:`NGHTTP2_ERR_PAUSE` is used, the return value includes the + * number of bytes which was used to produce the data or frame for the + * callback. + * + * This function returns the number of processed bytes, or one of the + * following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + * :enum:`NGHTTP2_ERR_FLOODED` + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, + const uint8_t *in, + size_t inlen); + +/** + * @function + * + * Puts back previously deferred DATA frame in the stream |stream_id| + * to the outbound queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist; or no deferred data exist. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns nonzero value if |session| wants to receive data from the + * remote peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero value if |session| wants to send data to the remote + * peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); + +/** + * @function + * + * Returns stream_user_data for the stream |stream_id|. The + * stream_user_data is provided by `nghttp2_submit_request()`, + * `nghttp2_submit_headers()` or + * `nghttp2_session_set_stream_user_data()`. Unless it is set using + * `nghttp2_session_set_stream_user_data()`, if the stream is + * initiated by the remote endpoint, stream_user_data is always + * ``NULL``. If the stream does not exist, this function returns + * ``NULL``. + */ +NGHTTP2_EXTERN void * +nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Sets the |stream_user_data| to the stream denoted by the + * |stream_id|. If a stream user data is already set to the stream, + * it is replaced with the |stream_user_data|. It is valid to specify + * ``NULL`` in the |stream_user_data|, which nullifies the associated + * data pointer. + * + * It is valid to set the |stream_user_data| to the stream reserved by + * PUSH_PROMISE frame. + * + * This function returns 0 if it succeeds, or one of following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist + */ +NGHTTP2_EXTERN int +nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, void *stream_user_data); + +/** + * @function + * + * Sets |user_data| to |session|, overwriting the existing user data + * specified in `nghttp2_session_client_new()`, or + * `nghttp2_session_server_new()`. + */ +NGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session, + void *user_data); + +/** + * @function + * + * Returns the number of frames in the outbound queue. This does not + * include the deferred DATA frames. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_outbound_queue_size(nghttp2_session *session); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for the stream |stream_id|. The local + * (receive) window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective data length. In particular, if the + * local window size is reduced by submitting negative + * window_size_increment with `nghttp2_submit_window_update()`, this + * function returns the number of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the local (receive) window size for the stream |stream_id|. + * The local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_stream_local_window_size()` to know the amount + * of data the remote endpoint can send without receiving stream level + * WINDOW_UPDATE frame. Note that each stream is still subject to the + * connection level flow control. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the amount of flow-controlled payload (e.g., DATA) that the + * remote endpoint can send without receiving stream level + * WINDOW_UPDATE frame. It is also subject to the connection level + * flow control. So the actual amount of data to send is + * min(`nghttp2_session_get_stream_local_window_size()`, + * `nghttp2_session_get_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for a connection. The local (receive) + * window size can be adjusted by `nghttp2_submit_window_update()`. + * This function takes into account that and returns effective data + * length. In particular, if the local window size is reduced by + * submitting negative window_size_increment with + * `nghttp2_submit_window_update()`, this function returns the number + * of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); + +/** + * @function + * + * Returns the local (receive) window size for a connection. The + * local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_local_window_size()` to know the amount of + * data the remote endpoint can send without receiving + * connection-level WINDOW_UPDATE frame. Note that each stream is + * still subject to the stream level flow control. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_effective_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the amount of flow-controlled payload (e.g., DATA) that the + * remote endpoint can send without receiving connection level + * WINDOW_UPDATE frame. Note that each stream is still subject to the + * stream level flow control (see + * `nghttp2_session_get_stream_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the remote window size for a given stream |stream_id|. + * + * This is the amount of flow-controlled payload (e.g., DATA) that the + * local endpoint can send without stream level WINDOW_UPDATE. There + * is also connection level flow control, so the effective size of + * payload that the local endpoint can actually send is + * min(`nghttp2_session_get_stream_remote_window_size()`, + * `nghttp2_session_get_remote_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_remote_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the remote window size for a connection. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_remote_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns 1 if local peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns 1 if remote peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns the current dynamic table size of HPACK inflater, including + * the overhead 32 bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session); + +/** + * @function + * + * Returns the current dynamic table size of HPACK deflater including + * the overhead 32 bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * The last stream ID is the minimum value between the stream ID of a + * stream for which :type:`nghttp2_on_frame_recv_callback` was called + * most recently and the last stream ID we have sent to the peer + * previously. + * + * The |error_code| is the error code of this GOAWAY frame. The + * pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * After the transmission, both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0. + * + * This function should be called when the connection should be + * terminated after sending GOAWAY. If the remaining streams should + * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * This function behaves like `nghttp2_session_terminate_session()`, + * but the last stream ID can be specified by the application for fine + * grained control of stream. The HTTP/2 specification does not allow + * last_stream_id to be increased. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id we have previously sent to + * the peer. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |last_stream_id| is invalid. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code); + +/** + * @function + * + * Signals to the client that the server started graceful shutdown + * procedure. + * + * This function is only usable for server. If this function is + * called with client side session, this function returns + * :enum:`NGHTTP2_ERR_INVALID_STATE`. + * + * To gracefully shutdown HTTP/2 session, server should call this + * function to send GOAWAY with last_stream_id (1u << 31) - 1. And + * after some delay (e.g., 1 RTT), send another GOAWAY with the stream + * ID that the server has some processing using + * `nghttp2_submit_goaway()`. See also + * `nghttp2_session_get_last_proc_stream_id()`. + * + * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY + * and does nothing more. This is a mere indication to the client + * that session shutdown is imminent. The application should call + * `nghttp2_submit_goaway()` with appropriate last_stream_id after + * this call. + * + * If one or more GOAWAY frame have been already sent by either + * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, + * this function has no effect. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + */ +NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); + +/** + * @function + * + * Returns the value of SETTINGS |id| notified by a remote endpoint. + * The |id| must be one of values defined in + * :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t nghttp2_session_get_remote_settings( + nghttp2_session *session, nghttp2_settings_id id); + +/** + * @function + * + * Returns the value of SETTINGS |id| of local endpoint acknowledged + * by the remote endpoint. The |id| must be one of the values defined + * in :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t nghttp2_session_get_local_settings( + nghttp2_session *session, nghttp2_settings_id id); + +/** + * @function + * + * Tells the |session| that next stream ID is |next_stream_id|. The + * |next_stream_id| must be equal or greater than the value returned + * by `nghttp2_session_get_next_stream_id()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |next_stream_id| is strictly less than the value + * `nghttp2_session_get_next_stream_id()` returns; or + * |next_stream_id| is invalid (e.g., even integer for client, or + * odd integer for server). + */ +NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id); + +/** + * @function + * + * Returns the next outgoing stream ID. Notice that return type is + * uint32_t. If we run out of stream ID for this session, this + * function returns 1 << 31. + */ +NGHTTP2_EXTERN uint32_t +nghttp2_session_get_next_stream_id(nghttp2_session *session); + +/** + * @function + * + * Tells the |session| that |size| bytes for a stream denoted by + * |stream_id| were consumed by application and are ready to + * WINDOW_UPDATE. The consumed bytes are counted towards both + * connection and stream level WINDOW_UPDATE (see + * `nghttp2_session_consume_connection()` and + * `nghttp2_session_consume_stream()` to update consumption + * independently). This function is intended to be used without + * automatic window update (see + * `nghttp2_option_set_no_auto_window_update()`). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, + int32_t stream_id, size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for connection level. Note that + * HTTP/2 maintains connection and stream level flow control windows + * independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, + size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for stream denoted by |stream_id|. + * Note that HTTP/2 maintains connection and stream level flow control + * windows independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, + int32_t stream_id, + size_t size); + +/** + * @function + * + * Changes priority of existing stream denoted by |stream_id|. The + * new priority specification is |pri_spec|. + * + * The priority is changed silently and instantly, and no PRIORITY + * frame will be sent to notify the peer of this change. This + * function may be useful for server to change the priority of pushed + * stream. + * + * If |session| is initialized as server, and ``pri_spec->stream_id`` + * points to the idle stream, the idle stream is created if it does + * not exist. The created idle stream will depend on root stream + * (stream 0) with weight 16. + * + * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not + * found, we use default priority instead of given |pri_spec|. That + * is make stream depend on root stream with weight 16. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * Attempted to depend on itself; or no stream exist for the given + * |stream_id|; or |stream_id| is 0 + */ +NGHTTP2_EXTERN int +nghttp2_session_change_stream_priority(nghttp2_session *session, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Creates idle stream with the given |stream_id|, and priority + * |pri_spec|. + * + * The stream creation is done without sending PRIORITY frame, which + * means that peer does not know about the existence of this idle + * stream in the local endpoint. + * + * RFC 7540 does not disallow the use of creation of idle stream with + * odd or even stream ID regardless of client or server. So this + * function can create odd or even stream ID regardless of client or + * server. But probably it is a bit safer to use the stream ID the + * local endpoint can initiate (in other words, use odd stream ID for + * client, and even stream ID for server), to avoid potential + * collision from peer's instruction. Also we can use + * `nghttp2_session_set_next_stream_id()` to avoid to open created + * idle streams accidentally if we follow this recommendation. + * + * If |session| is initialized as server, and ``pri_spec->stream_id`` + * points to the idle stream, the idle stream is created if it does + * not exist. The created idle stream will depend on root stream + * (stream 0) with weight 16. + * + * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not + * found, we use default priority instead of given |pri_spec|. That + * is make stream depend on root stream with weight 16. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * Attempted to depend on itself; or stream denoted by |stream_id| + * already exists; or |stream_id| cannot be used to create idle + * stream (in other words, local endpoint has already opened + * stream ID greater than or equal to the given stream ID; or + * |stream_id| is 0 + */ +NGHTTP2_EXTERN int +nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * .. warning:: + * + * This function is deprecated in favor of + * `nghttp2_session_upgrade2()`, because this function lacks the + * parameter to tell the library the request method used in the + * original HTTP request. This information is required for client + * to validate actual response body length against content-length + * header field (see `nghttp2_option_set_no_http_messaging()`). If + * HEAD is used in request, the length of response body must be 0 + * regardless of value included in content-length header field. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |settings_payload| is badly formed. + * :enum:`NGHTTP2_ERR_PROTO` + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data); + +/** + * @function + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * If the request method is HEAD, pass nonzero value to + * |head_request|. Otherwise, pass 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |settings_payload| is badly formed. + * :enum:`NGHTTP2_ERR_PROTO` + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + int head_request, + void *stream_user_data); + +/** + * @function + * + * Serializes the SETTINGS values |iv| in the |buf|. The size of the + * |buf| is specified by |buflen|. The number of entries in the |iv| + * array is given by |niv|. The required space in |buf| for the |niv| + * entries is ``6*niv`` bytes and if the given buffer is too small, an + * error is returned. This function is used mainly for creating a + * SETTINGS payload to be sent with the ``HTTP2-Settings`` header + * field in an HTTP Upgrade request. The data written in |buf| is NOT + * base64url encoded and the application is responsible for encoding. + * + * This function returns the number of bytes written in |buf|, or one + * of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains duplicate settings ID or invalid value. + * + * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_pack_settings_payload( + uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv); + +/** + * @function + * + * Returns string describing the |lib_error_code|. The + * |lib_error_code| must be one of the :enum:`nghttp2_error`. + */ +NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); + +/** + * @function + * + * Returns string representation of HTTP/2 error code |error_code| + * (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code == + * NGHTTP2_PROTOCOL_ERROR``). If string representation is unknown for + * given |error_code|, this function returns string ``unknown``. + */ +NGHTTP2_EXTERN const char *nghttp2_http2_strerror(uint32_t error_code); + +/** + * @function + * + * Initializes |pri_spec| with the |stream_id| of the stream to depend + * on with |weight| and its exclusive flag. If |exclusive| is + * nonzero, exclusive flag is set. + * + * The |weight| must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. + */ +NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, + int32_t weight, int exclusive); + +/** + * @function + * + * Initializes |pri_spec| with the default values. The default values + * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and + * exclusive = 0. + */ +NGHTTP2_EXTERN void +nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Returns nonzero if the |pri_spec| is filled with default values. + */ +NGHTTP2_EXTERN int +nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Submits HEADERS frame and optionally one or more DATA frames. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes + * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * request HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. In this case, a method that allows + * request message bodies + * (https://tools.ietf.org/html/rfc7231#section-4) must be specified + * with ``:method`` key in |nva| (e.g. ``POST``). This function does + * not take ownership of the |data_prd|. The function copies the + * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have + * END_STREAM set. The |stream_user_data| is data associated to the + * stream opened by this request and can be an arbitrary pointer, + * which can be retrieved later by + * `nghttp2_session_get_stream_user_data()`. + * + * This function returns assigned stream ID if it succeeds, or one of + * the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * Trying to depend on itself (new stream ID equals + * ``pri_spec->stream_id``). + * :enum:`NGHTTP2_ERR_PROTO` + * The |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds. But + * that stream is not created yet. The application must not submit + * frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. This means `nghttp2_session_get_stream_user_data()` does + * not work before the callback. But + * `nghttp2_session_set_stream_user_data()` handles this situation + * specially, and it can set data to a stream during this period. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_request( + nghttp2_session *session, const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, + void *stream_user_data); + +/** + * @function + * + * Submits response HEADERS frame and optionally one or more DATA + * frames against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * response HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. This function does not take ownership + * of the |data_prd|. The function copies the members of the + * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have + * END_STREAM flag set. + * + * This method can be used as normal HTTP response and push response. + * When pushing a resource using this function, the |session| must be + * configured using `nghttp2_session_server_new()` or its variants and + * the target stream denoted by the |stream_id| must be reserved using + * `nghttp2_submit_push_promise()`. + * + * To send non-final response headers (e.g., HTTP status 101), don't + * use this function because this function half-closes the outbound + * stream. Instead, use `nghttp2_submit_headers()` for this purpose. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. Normally, this does not happen, but when + * application wrongly calls `nghttp2_submit_response()` twice, + * this may happen. + * :enum:`NGHTTP2_ERR_PROTO` + * The |session| is client session. + * + * .. warning:: + * + * Calling this function twice for the same stream ID may lead to + * program crash. It is generally considered to a programming error + * to commit response twice. + */ +NGHTTP2_EXTERN int +nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd); + +/** + * @function + * + * Submits trailer fields HEADERS against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application must not include pseudo-header + * fields (headers whose names starts with ":") in |nva|. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * For server, trailer fields must follow response HEADERS or response + * DATA without END_STREAM flat set. The library does not enforce + * this requirement, and applications should do this for themselves. + * If `nghttp2_submit_trailer()` is called before any response HEADERS + * submission (usually by `nghttp2_submit_response()`), the content of + * |nva| will be sent as response headers, which will result in error. + * + * This function has the same effect with `nghttp2_submit_headers()`, + * with flags = :enum:`NGHTTP2_FLAG_END_STREAM` and both pri_spec and + * stream_user_data to NULL. + * + * To submit trailer fields after `nghttp2_submit_response()` is + * called, the application has to specify + * :type:`nghttp2_data_provider` to `nghttp2_submit_response()`. + * Inside of :type:`nghttp2_data_source_read_callback`, when setting + * :enum:`NGHTTP2_DATA_FLAG_EOF`, also set + * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the + * application can send trailer fields using + * `nghttp2_submit_trailer()`. `nghttp2_submit_trailer()` can be used + * inside :type:`nghttp2_data_source_read_callback`. + * + * This function returns 0 if it succeeds and |stream_id| is -1. + * Otherwise, this function returns 0 if it succeeds, or one of the + * following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Submits HEADERS frame. The |flags| is bitwise OR of the + * following values: + * + * * :enum:`NGHTTP2_FLAG_END_STREAM` + * + * If |flags| includes :enum:`NGHTTP2_FLAG_END_STREAM`, this frame has + * END_STREAM flag set. + * + * The library handles the CONTINUATION frame internally and it + * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE + * or CONTINUATION frame. + * + * If the |stream_id| is -1, this frame is assumed as request (i.e., + * request HEADERS frame which opens new stream). In this case, the + * assigned stream ID will be returned. Otherwise, specify stream ID + * in |stream_id|. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes + * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * The |stream_user_data| is a pointer to an arbitrary data which is + * associated to the stream this frame will open. Therefore it is + * only used if this frame opens streams, in other words, it changes + * stream state from idle or reserved to open. + * + * This function is low-level in a sense that the application code can + * specify flags directly. For usual HTTP request, + * `nghttp2_submit_request()` is useful. Likewise, for HTTP response, + * prefer `nghttp2_submit_response()`. + * + * This function returns newly assigned stream ID if it succeeds and + * |stream_id| is -1. Otherwise, this function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; or trying to depend on itself (stream ID + * equals ``pri_spec->stream_id``). + * :enum:`NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. This happens if stream denoted by |stream_id| + * is in reserved state. + * :enum:`NGHTTP2_ERR_PROTO` + * The |stream_id| is -1, and |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds and + * |stream_id| is -1. But that stream is not opened yet. The + * application must not submit frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_headers( + nghttp2_session *session, uint8_t flags, int32_t stream_id, + const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data); + +/** + * @function + * + * Submits one or more DATA frames to the stream |stream_id|. The + * data to be sent are provided by |data_prd|. If |flags| contains + * :enum:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM + * flag set. + * + * This function does not take ownership of the |data_prd|. The + * function copies the members of the |data_prd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_STREAM_CLOSED` + * The stream was already closed; or the |stream_id| is invalid. + * + * .. note:: + * + * Currently, only one DATA or HEADERS is allowed for a stream at a + * time. Submitting these frames more than once before first DATA + * or HEADERS is finished results in :enum:`NGHTTP2_ERR_DATA_EXIST` + * error code. The earliest callback which tells that previous + * frame is done is :type:`nghttp2_on_frame_send_callback`. In side + * that callback, new data can be submitted using + * `nghttp2_submit_data()`. Of course, all data except for last one + * must not have :enum:`NGHTTP2_FLAG_END_STREAM` flag set in + * |flags|. This sounds a bit complicated, and we recommend to use + * `nghttp2_submit_request()` and `nghttp2_submit_response()` to + * avoid this cascading issue. The experience shows that for HTTP + * use, these two functions are enough to implement both client and + * server. + */ +NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd); + +/** + * @function + * + * Submits PRIORITY frame to change the priority of stream |stream_id| + * to the priority specification |pri_spec|. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * is not allowed for this function. To specify the priority, use + * `nghttp2_priority_spec_init()`. This function will copy its data + * members. + * + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes + * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to + * depend on itself. + */ +NGHTTP2_EXTERN int +nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Submits RST_STREAM frame to cancel/reject the stream |stream_id| + * with the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + uint32_t error_code); + +/** + * @function + * + * Stores local settings and submits SETTINGS frame. The |iv| is the + * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| + * indicates the number of :type:`nghttp2_settings_entry`. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * This function does not take ownership of the |iv|. This function + * copies all the elements in the |iv|. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * + * SETTINGS with :enum:`NGHTTP2_FLAG_ACK` is automatically submitted + * by the library and application could not send it at its will. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains invalid value (e.g., initial window size + * strictly greater than (1 << 31) - 1. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, + uint8_t flags, + const nghttp2_settings_entry *iv, + size_t niv); + +/** + * @function + * + * Submits PUSH_PROMISE frame. + * + * The |flags| is currently ignored. The library handles the + * CONTINUATION frame internally and it correctly sets END_HEADERS to + * the last sequence of the PUSH_PROMISE or CONTINUATION frame. + * + * The |stream_id| must be client initiated stream ID. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name + * and value are not copied respectively. With + * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to + * pass header field name in lowercase. The application should + * maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * The |promised_stream_user_data| is a pointer to an arbitrary data + * which is associated to the promised stream this frame will open and + * make it in reserved state. It is available using + * `nghttp2_session_get_stream_user_data()`. The application can + * access it in :type:`nghttp2_before_frame_send_callback` and + * :type:`nghttp2_on_frame_send_callback` of this frame. + * + * The client side is not allowed to use this function. + * + * To submit response headers and data, use + * `nghttp2_submit_response()`. + * + * This function returns assigned promised stream ID if it succeeds, + * or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_PROTO` + * This function was invoked when |session| is initialized as + * client. + * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; The |stream_id| does not designate stream + * that peer initiated. + * :enum:`NGHTTP2_ERR_STREAM_CLOSED` + * The stream was already closed; or the |stream_id| is invalid. + * + * .. warning:: + * + * This function returns assigned promised stream ID if it succeeds. + * As of 1.16.0, stream object for pushed resource is created when + * this function succeeds. In that case, the application can submit + * push response for the promised frame. + * + * In 1.15.0 or prior versions, pushed stream is not opened yet when + * this function succeeds. The application must not submit frame to + * that stream ID before :type:`nghttp2_before_frame_send_callback` + * is called for this frame. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_push_promise( + nghttp2_session *session, uint8_t flags, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data); + +/** + * @function + * + * Submits PING frame. You don't have to send PING back when you + * received PING frame. The library automatically submits PING frame + * in this case. + * + * The |flags| is bitwise OR of 0 or more of the following value. + * + * * :enum:`NGHTTP2_FLAG_ACK` + * + * Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags| + * should be :enum:`NGHTTP2_FLAG_NONE`. + * + * If the |opaque_data| is non ``NULL``, then it should point to the 8 + * bytes array of memory to specify opaque data to send with PING + * frame. If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will + * be sent as opaque data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/** + * @function + * + * Submits GOAWAY frame with the last stream ID |last_stream_id| and + * the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * The HTTP/2 specification says last_stream_id must not be increased + * from the value previously sent. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id previously sent to the + * peer. + * + * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not + * zero, those data will be sent as additional debug data. The + * library makes a copy of the memory region pointed by |opaque_data| + * with the length |opaque_data_len|, so the caller does not need to + * keep this memory after the return of this function. If the + * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. + * + * After successful transmission of GOAWAY, following things happen. + * All incoming streams having strictly more than |last_stream_id| are + * closed. All incoming HEADERS which starts new stream are simply + * ignored. After all active streams are handled, both + * `nghttp2_session_want_read()` and `nghttp2_session_want_write()` + * return 0 and the application can close session. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |opaque_data_len| is too large; the |last_stream_id| is + * invalid. + */ +NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, + uint8_t flags, int32_t last_stream_id, + uint32_t error_code, + const uint8_t *opaque_data, + size_t opaque_data_len); + +/** + * @function + * + * Returns the last stream ID of a stream for which + * :type:`nghttp2_on_frame_recv_callback` was invoked most recently. + * The returned value can be used as last_stream_id parameter for + * `nghttp2_submit_goaway()` and + * `nghttp2_session_terminate_session2()`. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero if new request can be sent from local endpoint. + * + * This function return 0 if request is not allowed for this session. + * There are several reasons why request is not allowed. Some of the + * reasons are: session is server; stream ID has been spent; GOAWAY + * has been sent or received. + * + * The application can call `nghttp2_submit_request()` without + * consulting this function. In that case, `nghttp2_submit_request()` + * may return error. Or, request is failed to sent, and + * :type:`nghttp2_on_stream_close_callback` is called. + */ +NGHTTP2_EXTERN int +nghttp2_session_check_request_allowed(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero if |session| is initialized as server side session. + */ +NGHTTP2_EXTERN int +nghttp2_session_check_server_session(nghttp2_session *session); + +/** + * @function + * + * Submits WINDOW_UPDATE frame. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To + * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. + * + * If the |window_size_increment| is positive, the WINDOW_UPDATE with + * that value as window_size_increment is queued. If the + * |window_size_increment| is larger than the received bytes from the + * remote endpoint, the local window size is increased by that + * difference. If the sole purpose is to increase the local window + * size, consider to use `nghttp2_session_set_local_window_size()`. + * + * If the |window_size_increment| is negative, the local window size + * is decreased by -|window_size_increment|. If automatic + * WINDOW_UPDATE is enabled + * (`nghttp2_option_set_no_auto_window_update()`), and the library + * decided that the WINDOW_UPDATE should be submitted, then + * WINDOW_UPDATE is queued with the current received bytes count. If + * the sole purpose is to decrease the local window size, consider to + * use `nghttp2_session_set_local_window_size()`. + * + * If the |window_size_increment| is 0, the function does nothing and + * returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_FLOW_CONTROL` + * The local window size overflow or gets negative. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/** + * @function + * + * Set local window size (local endpoints's window size) to the given + * |window_size| for the given stream denoted by |stream_id|. To + * change connection level window size, specify 0 to |stream_id|. To + * increase window size, this function may submit WINDOW_UPDATE frame + * to transmission queue. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * This sounds similar to `nghttp2_submit_window_update()`, but there + * are 2 differences. The first difference is that this function + * takes the absolute value of window size to set, rather than the + * delta. To change the window size, this may be easier to use since + * the application just declares the intended window size, rather than + * calculating delta. The second difference is that + * `nghttp2_submit_window_update()` affects the received bytes count + * which has not acked yet. By the specification of + * `nghttp2_submit_window_update()`, to strictly increase the local + * window size, we have to submit delta including all received bytes + * count, which might not be desirable in some cases. On the other + * hand, this function does not affect the received bytes count. It + * just sets the local window size to the given value. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is negative. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags, + int32_t stream_id, int32_t window_size); + +/** + * @function + * + * Submits extension frame. + * + * Application can pass arbitrary frame flags and stream ID in |flags| + * and |stream_id| respectively. The |payload| is opaque pointer, and + * it can be accessible though ``frame->ext.payload`` in + * :type:`nghttp2_pack_extension_callback`. The library will not own + * passed |payload| pointer. + * + * The application must set :type:`nghttp2_pack_extension_callback` + * using `nghttp2_session_callbacks_set_pack_extension_callback()`. + * + * The application should retain the memory pointed by |payload| until + * the transmission of extension frame is done (which is indicated by + * :type:`nghttp2_on_frame_send_callback`), or transmission fails + * (which is indicated by :type:`nghttp2_on_frame_not_send_callback`). + * If application does not touch this memory region after packing it + * into a wire format, application can free it inside + * :type:`nghttp2_pack_extension_callback`. + * + * The standard HTTP/2 frame cannot be sent with this function, so + * |type| must be strictly grater than 0x9. Otherwise, this function + * will fail with error code :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * If :type:`nghttp2_pack_extension_callback` is not set. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * If |type| specifies standard HTTP/2 frame type. The frame + * types in the rage [0x0, 0x9], both inclusive, are standard + * HTTP/2 frame type, and cannot be sent using this function. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory + */ +NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session, + uint8_t type, uint8_t flags, + int32_t stream_id, void *payload); + +/** + * @struct + * + * The payload of ALTSVC frame. ALTSVC frame is a non-critical + * extension to HTTP/2. If this frame is received, and + * `nghttp2_option_set_user_recv_extension_type()` is not set, and + * `nghttp2_option_set_builtin_recv_extension_type()` is set for + * :enum:`NGHTTP2_ALTSVC`, ``nghttp2_extension.payload`` will point to + * this struct. + * + * It has the following members: + */ +typedef struct { + /** + * The pointer to origin which this alternative service is + * associated with. This is not necessarily NULL-terminated. + */ + uint8_t *origin; + /** + * The length of the |origin|. + */ + size_t origin_len; + /** + * The pointer to Alt-Svc field value contained in ALTSVC frame. + * This is not necessarily NULL-terminated. + */ + uint8_t *field_value; + /** + * The length of the |field_value|. + */ + size_t field_value_len; +} nghttp2_ext_altsvc; + +/** + * @function + * + * Submits ALTSVC frame. + * + * ALTSVC frame is a non-critical extension to HTTP/2, and defined in + * `RFC 7383 `_. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |origin| points to the origin this alternative service is + * associated with. The |origin_len| is the length of the origin. If + * |stream_id| is 0, the origin must be specified. If |stream_id| is + * not zero, the origin must be empty (in other words, |origin_len| + * must be 0). + * + * The ALTSVC frame is only usable from server side. If this function + * is invoked with client side session, this function returns + * :enum:`NGHTTP2_ERR_INVALID_STATE`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * The function is called from client side session + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The sum of |origin_len| and |field_value_len| is larger than + * 16382; or |origin_len| is 0 while |stream_id| is 0; or + * |origin_len| is not 0 while |stream_id| is not 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + const uint8_t *origin, + size_t origin_len, + const uint8_t *field_value, + size_t field_value_len); + +/** + * @struct + * + * The single entry of an origin. + */ +typedef struct { + /** + * The pointer to origin. No validation is made against this field + * by the library. This is not necessarily NULL-terminated. + */ + uint8_t *origin; + /** + * The length of the |origin|. + */ + size_t origin_len; +} nghttp2_origin_entry; + +/** + * @struct + * + * The payload of ORIGIN frame. ORIGIN frame is a non-critical + * extension to HTTP/2 and defined by `RFC 8336 + * `_. + * + * If this frame is received, and + * `nghttp2_option_set_user_recv_extension_type()` is not set, and + * `nghttp2_option_set_builtin_recv_extension_type()` is set for + * :enum:`NGHTTP2_ORIGIN`, ``nghttp2_extension.payload`` will point to + * this struct. + * + * It has the following members: + */ +typedef struct { + /** + * The number of origins contained in |ov|. + */ + size_t nov; + /** + * The pointer to the array of origins contained in ORIGIN frame. + */ + nghttp2_origin_entry *ov; +} nghttp2_ext_origin; + +/** + * @function + * + * Submits ORIGIN frame. + * + * ORIGIN frame is a non-critical extension to HTTP/2 and defined by + * `RFC 8336 `_. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |ov| points to the array of origins. The |nov| specifies the + * number of origins included in |ov|. This function creates copies + * of all elements in |ov|. + * + * The ORIGIN frame is only usable by a server. If this function is + * invoked with client side session, this function returns + * :enum:`NGHTTP2_ERR_INVALID_STATE`. + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * The function is called from client side session. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * There are too many origins, or an origin is too large to fit + * into a default frame payload. + */ +NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session, + uint8_t flags, + const nghttp2_origin_entry *ov, + size_t nov); + +/** + * @function + * + * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and + * ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative + * integer if ``lhs->name`` is found to be less than ``rhs->name``; or + * returns positive integer if ``lhs->name`` is found to be greater + * than ``rhs->name``; or returns 0 otherwise. + */ +NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, + const nghttp2_nv *rhs); + +/** + * @function + * + * A helper function for dealing with NPN in client side or ALPN in + * server side. The |in| contains peer's protocol list in preferable + * order. The format of |in| is length-prefixed and not + * null-terminated. For example, ``h2`` and + * ``http/1.1`` stored in |in| like this:: + * + * in[0] = 2 + * in[1..2] = "h2" + * in[3] = 8 + * in[4..11] = "http/1.1" + * inlen = 12 + * + * The selection algorithm is as follows: + * + * 1. If peer's list contains HTTP/2 protocol the library supports, + * it is selected and returns 1. The following step is not taken. + * + * 2. If peer's list contains ``http/1.1``, this function selects + * ``http/1.1`` and returns 0. The following step is not taken. + * + * 3. This function selects nothing and returns -1 (So called + * non-overlap case). In this case, |out| and |outlen| are left + * untouched. + * + * Selecting ``h2`` means that ``h2`` is written into |*out| and its + * length (which is 2) is assigned to |*outlen|. + * + * For ALPN, refer to https://tools.ietf.org/html/rfc7301 + * + * See http://technotes.googlecode.com/git/nextprotoneg.html for more + * details about NPN. + * + * For NPN, to use this method you should do something like:: + * + * static int select_next_proto_cb(SSL* ssl, + * unsigned char **out, + * unsigned char *outlen, + * const unsigned char *in, + * unsigned int inlen, + * void *arg) + * { + * int rv; + * rv = nghttp2_select_next_protocol(out, outlen, in, inlen); + * if (rv == -1) { + * return SSL_TLSEXT_ERR_NOACK; + * } + * if (rv == 1) { + * ((MyType*)arg)->http2_selected = 1; + * } + * return SSL_TLSEXT_ERR_OK; + * } + * ... + * SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); + * + */ +NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen); + +/** + * @function + * + * Returns a pointer to a nghttp2_info struct with version information + * about the run-time library in use. The |least_version| argument + * can be set to a 24 bit numerical value for the least accepted + * version number and if the condition is not met, this function will + * return a ``NULL``. Pass in 0 to skip the version checking. + */ +NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); + +/** + * @function + * + * Returns nonzero if the :type:`nghttp2_error` library error code + * |lib_error| is fatal. + */ +NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code); + +/** + * @function + * + * Returns nonzero if HTTP header field name |name| of length |len| is + * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 + * + * Because this is a header field name in HTTP2, the upper cased alphabet + * is treated as error. + */ +NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); + +/** + * @function + * + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + */ +NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); + +/** + * @function + * + * Returns nonzero if the |value| which is supposed to the value of + * :authority or host header field is valid according to + * https://tools.ietf.org/html/rfc3986#section-3.2 + * + * |value| is valid if it merely consists of the allowed characters. + * In particular, it does not check whether |value| follows the syntax + * of authority. + */ +NGHTTP2_EXTERN int nghttp2_check_authority(const uint8_t *value, size_t len); + +/* HPACK API */ + +struct nghttp2_hd_deflater; + +/** + * @struct + * + * HPACK deflater object. + */ +typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; + +/** + * @function + * + * Initializes |*deflater_ptr| for deflating name/values pairs. + * + * The |max_deflate_dynamic_table_size| is the upper bound of header + * table size the deflater will use. + * + * If this function fails, |*deflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t max_deflate_dynamic_table_size); + +/** + * @function + * + * Like `nghttp2_hd_deflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_deflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t max_deflate_dynamic_table_size, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |deflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Changes header table size of the |deflater| to + * |settings_max_dynamic_table_size| bytes. This may trigger eviction + * in the dynamic table. + * + * The |settings_max_dynamic_table_size| should be the value received + * in SETTINGS_HEADER_TABLE_SIZE. + * + * The deflater never uses more memory than + * ``max_deflate_dynamic_table_size`` bytes specified in + * `nghttp2_hd_deflate_new()`. Therefore, if + * |settings_max_dynamic_table_size| > + * ``max_deflate_dynamic_table_size``, resulting maximum table size + * becomes ``max_deflate_dynamic_table_size``. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t settings_max_dynamic_table_size); + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |buf| of length |buflen|. + * + * If |buf| is not large enough to store the deflated header block, + * this function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The + * caller should use `nghttp2_hd_deflate_bound()` to know the upper + * bound of buffer size required to deflate given header name/value + * pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |buf| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nva, + size_t nvlen); + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |veclen| size of buf vector |vec|. The each size of buffer + * must be set in len field of :type:`nghttp2_vec`. If and only if + * one chunk is filled up completely, next chunk will be used. If + * |vec| is not large enough to store the deflated header block, this + * function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |vec| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, + const nghttp2_vec *vec, + size_t veclen, + const nghttp2_nv *nva, + size_t nvlen); + +/** + * @function + * + * Returns an upper bound on the compressed size after deflation of + * |nva| of length |nvlen|. + */ +NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, + size_t nvlen); + +/** + * @function + * + * Returns the number of entries that header table of |deflater| + * contains. This is the sum of the number of static table and + * dynamic table, so the return value is at least 61. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Returns the table entry denoted by |idx| from header table of + * |deflater|. The |idx| is 1-based, and idx=1 returns first entry of + * static table. idx=62 returns first entry of dynamic table if it + * exists. Specifying idx=0 is error, and this function returns NULL. + * If |idx| is strictly greater than the number of entries the tables + * contain, this function returns NULL. + */ +NGHTTP2_EXTERN +const nghttp2_nv * +nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx); + +/** + * @function + * + * Returns the used dynamic table size, including the overhead 32 + * bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Returns the maximum dynamic table size. + */ +NGHTTP2_EXTERN +size_t +nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater); + +struct nghttp2_hd_inflater; + +/** + * @struct + * + * HPACK inflater object. + */ +typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; + +/** + * @function + * + * Initializes |*inflater_ptr| for inflating name/values pairs. + * + * If this function fails, |*inflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); + +/** + * @function + * + * Like `nghttp2_hd_inflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_inflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |inflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Changes header table size in the |inflater|. This may trigger + * eviction in the dynamic table. + * + * The |settings_max_dynamic_table_size| should be the value + * transmitted in SETTINGS_HEADER_TABLE_SIZE. + * + * This function must not be called while header block is being + * inflated. In other words, this function must be called after + * initialization of |inflater|, but before calling + * `nghttp2_hd_inflate_hd2()`, or after + * `nghttp2_hd_inflate_end_headers()`. Otherwise, + * `NGHTTP2_ERR_INVALID_STATE` was returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * The function is called while header block is being inflated. + * Probably, application missed to call + * `nghttp2_hd_inflate_end_headers()`. + */ +NGHTTP2_EXTERN int +nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, + size_t settings_max_dynamic_table_size); + +/** + * @enum + * + * The flags for header inflation. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_HD_INFLATE_NONE = 0, + /** + * Indicates all headers were inflated. + */ + NGHTTP2_HD_INFLATE_FINAL = 0x01, + /** + * Indicates a header was emitted. + */ + NGHTTP2_HD_INFLATE_EMIT = 0x02 +} nghttp2_hd_inflate_flag; + +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_hd_inflate_hd2()` instead. + * + * Inflates name/value block stored in |in| with length |inlen|. This + * function performs decompression. For each successful emission of + * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in + * |*inflate_flags| and name/value pair is assigned to the |nv_out| + * and the function returns. The caller must not free the members of + * |nv_out|. + * + * The |nv_out| may include pointers to the memory region in the |in|. + * The caller must retain the |in| while the |nv_out| is used. + * + * The application should call this function repeatedly until the + * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + * return value is non-negative. This means the all input values are + * processed successfully. Then the application must call + * `nghttp2_hd_inflate_end_headers()` to prepare for the next header + * block input. + * + * The caller can feed complete compressed header block. It also can + * feed it in several chunks. The caller must set |in_final| to + * nonzero if the given input is the last block of the compressed + * header. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_HEADER_COMP` + * Inflation process has failed. + * :enum:`NGHTTP2_ERR_BUFFER_ERROR` + * The header field name or value is too large. + * + * Example follows:: + * + * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + * uint8_t *in, size_t inlen, int final) + * { + * ssize_t rv; + * + * for(;;) { + * nghttp2_nv nv; + * int inflate_flags = 0; + * + * rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, + * in, inlen, final); + * + * if(rv < 0) { + * fprintf(stderr, "inflate failed with error code %zd", rv); + * return -1; + * } + * + * in += rv; + * inlen -= rv; + * + * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + * fwrite(nv.name, nv.namelen, 1, stderr); + * fprintf(stderr, ": "); + * fwrite(nv.value, nv.valuelen, 1, stderr); + * fprintf(stderr, "\n"); + * } + * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + * nghttp2_hd_inflate_end_headers(hd_inflater); + * break; + * } + * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + * inlen == 0) { + * break; + * } + * } + * + * return 0; + * } + * + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, + int *inflate_flags, uint8_t *in, + size_t inlen, int in_final); + +/** + * @function + * + * Inflates name/value block stored in |in| with length |inlen|. This + * function performs decompression. For each successful emission of + * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in + * |*inflate_flags| and name/value pair is assigned to the |nv_out| + * and the function returns. The caller must not free the members of + * |nv_out|. + * + * The |nv_out| may include pointers to the memory region in the |in|. + * The caller must retain the |in| while the |nv_out| is used. + * + * The application should call this function repeatedly until the + * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + * return value is non-negative. If that happens, all given input + * data (|inlen| bytes) are processed successfully. Then the + * application must call `nghttp2_hd_inflate_end_headers()` to prepare + * for the next header block input. + * + * In other words, if |in_final| is nonzero, and this function returns + * |inlen|, you can assert that :enum:`NGHTTP2_HD_INFLATE_FINAL` is + * set in |*inflate_flags|. + * + * The caller can feed complete compressed header block. It also can + * feed it in several chunks. The caller must set |in_final| to + * nonzero if the given input is the last block of the compressed + * header. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_HEADER_COMP` + * Inflation process has failed. + * :enum:`NGHTTP2_ERR_BUFFER_ERROR` + * The header field name or value is too large. + * + * Example follows:: + * + * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + * uint8_t *in, size_t inlen, int final) + * { + * ssize_t rv; + * + * for(;;) { + * nghttp2_nv nv; + * int inflate_flags = 0; + * + * rv = nghttp2_hd_inflate_hd2(hd_inflater, &nv, &inflate_flags, + * in, inlen, final); + * + * if(rv < 0) { + * fprintf(stderr, "inflate failed with error code %zd", rv); + * return -1; + * } + * + * in += rv; + * inlen -= rv; + * + * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + * fwrite(nv.name, nv.namelen, 1, stderr); + * fprintf(stderr, ": "); + * fwrite(nv.value, nv.valuelen, 1, stderr); + * fprintf(stderr, "\n"); + * } + * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + * nghttp2_hd_inflate_end_headers(hd_inflater); + * break; + * } + * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + * inlen == 0) { + * break; + * } + * } + * + * return 0; + * } + * + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, + int *inflate_flags, + const uint8_t *in, size_t inlen, + int in_final); + +/** + * @function + * + * Signals the end of decompression for one header block. + * + * This function returns 0 if it succeeds. Currently this function + * always succeeds. + */ +NGHTTP2_EXTERN int +nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Returns the number of entries that header table of |inflater| + * contains. This is the sum of the number of static table and + * dynamic table, so the return value is at least 61. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Returns the table entry denoted by |idx| from header table of + * |inflater|. The |idx| is 1-based, and idx=1 returns first entry of + * static table. idx=62 returns first entry of dynamic table if it + * exists. Specifying idx=0 is error, and this function returns NULL. + * If |idx| is strictly greater than the number of entries the tables + * contain, this function returns NULL. + */ +NGHTTP2_EXTERN +const nghttp2_nv * +nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx); + +/** + * @function + * + * Returns the used dynamic table size, including the overhead 32 + * bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Returns the maximum dynamic table size. + */ +NGHTTP2_EXTERN +size_t +nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater); + +struct nghttp2_stream; + +/** + * @struct + * + * The structure to represent HTTP/2 stream. The details of this + * structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_stream nghttp2_stream; + +/** + * @function + * + * Returns pointer to :type:`nghttp2_stream` object denoted by + * |stream_id|. If stream was not found, returns NULL. + * + * Returns imaginary root stream (see + * `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|. + * + * Unless |stream_id| == 0, the returned pointer is valid until next + * call of `nghttp2_session_send()`, `nghttp2_session_mem_send()`, + * `nghttp2_session_recv()`, and `nghttp2_session_mem_recv()`. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id); + +/** + * @enum + * + * State of stream as described in RFC 7540. + */ +typedef enum { + /** + * idle state. + */ + NGHTTP2_STREAM_STATE_IDLE = 1, + /** + * open state. + */ + NGHTTP2_STREAM_STATE_OPEN, + /** + * reserved (local) state. + */ + NGHTTP2_STREAM_STATE_RESERVED_LOCAL, + /** + * reserved (remote) state. + */ + NGHTTP2_STREAM_STATE_RESERVED_REMOTE, + /** + * half closed (local) state. + */ + NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL, + /** + * half closed (remote) state. + */ + NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE, + /** + * closed state. + */ + NGHTTP2_STREAM_STATE_CLOSED +} nghttp2_stream_proto_state; + +/** + * @function + * + * Returns state of |stream|. The root stream retrieved by + * `nghttp2_session_get_root_stream()` will have stream state + * :enum:`NGHTTP2_STREAM_STATE_IDLE`. + */ +NGHTTP2_EXTERN nghttp2_stream_proto_state +nghttp2_stream_get_state(nghttp2_stream *stream); + +/** + * @function + * + * Returns root of dependency tree, which is imaginary stream with + * stream ID 0. The returned pointer is valid until |session| is + * freed by `nghttp2_session_del()`. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_session_get_root_stream(nghttp2_session *session); + +/** + * @function + * + * Returns the parent stream of |stream| in dependency tree. Returns + * NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_parent(nghttp2_stream *stream); + +NGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream); + +/** + * @function + * + * Returns the next sibling stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_next_sibling(nghttp2_stream *stream); + +/** + * @function + * + * Returns the previous sibling stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_previous_sibling(nghttp2_stream *stream); + +/** + * @function + * + * Returns the first child stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_first_child(nghttp2_stream *stream); + +/** + * @function + * + * Returns dependency weight to the parent stream of |stream|. + */ +NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream); + +/** + * @function + * + * Returns the sum of the weight for |stream|'s children. + */ +NGHTTP2_EXTERN int32_t +nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream); + +/** + * @functypedef + * + * Callback function invoked when the library outputs debug logging. + * The function is called with arguments suitable for ``vfprintf(3)`` + * + * The debug output is only enabled if the library is built with + * ``DEBUGBUILD`` macro defined. + */ +typedef void (*nghttp2_debug_vprintf_callback)(const char *format, + va_list args); + +/** + * @function + * + * Sets a debug output callback called by the library when built with + * ``DEBUGBUILD`` macro defined. If this option is not used, debug + * log is written into standard error output. + * + * For builds without ``DEBUGBUILD`` macro defined, this function is + * noop. + * + * Note that building with ``DEBUGBUILD`` may cause significant + * performance penalty to libnghttp2 because of extra processing. It + * should be used for debugging purpose only. + * + * .. Warning:: + * + * Building with ``DEBUGBUILD`` may cause significant performance + * penalty to libnghttp2 because of extra processing. It should be + * used for debugging purpose only. We write this two times because + * this is important. + */ +NGHTTP2_EXTERN void nghttp2_set_debug_vprintf_callback( + nghttp2_debug_vprintf_callback debug_vprintf_callback); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP2_H */ diff --git a/Utilities/cmnghttp2/lib/includes/nghttp2/nghttp2ver.h b/Utilities/cmnghttp2/lib/includes/nghttp2/nghttp2ver.h new file mode 100644 index 00000000000..45d21e2645c --- /dev/null +++ b/Utilities/cmnghttp2/lib/includes/nghttp2/nghttp2ver.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2VER_H +#define NGHTTP2VER_H + +/** + * @macro + * Version number of the nghttp2 library release + */ +#define NGHTTP2_VERSION "1.40.0" + +/** + * @macro + * Numerical representation of the version number of the nghttp2 library + * release. This is a 24 bit number with 8 bits for major number, 8 bits + * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. + */ +#define NGHTTP2_VERSION_NUM 0x012800 + +#endif /* NGHTTP2VER_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_buf.c b/Utilities/cmnghttp2/lib/nghttp2_buf.c new file mode 100644 index 00000000000..2a435bebf92 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_buf.c @@ -0,0 +1,525 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_buf.h" + +#include + +#include "nghttp2_helper.h" +#include "nghttp2_debug.h" + +void nghttp2_buf_init(nghttp2_buf *buf) { + buf->begin = NULL; + buf->end = NULL; + buf->pos = NULL; + buf->last = NULL; + buf->mark = NULL; +} + +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) { + nghttp2_buf_init(buf); + return nghttp2_buf_reserve(buf, initial, mem); +} + +void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) { + if (buf == NULL) { + return; + } + + nghttp2_mem_free(mem, buf->begin); + buf->begin = NULL; +} + +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) { + uint8_t *ptr; + size_t cap; + + cap = nghttp2_buf_cap(buf); + + if (cap >= new_cap) { + return 0; + } + + new_cap = nghttp2_max(new_cap, cap * 2); + + ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap); + if (ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + buf->pos = ptr + (buf->pos - buf->begin); + buf->last = ptr + (buf->last - buf->begin); + buf->mark = ptr + (buf->mark - buf->begin); + buf->begin = ptr; + buf->end = ptr + new_cap; + + return 0; +} + +void nghttp2_buf_reset(nghttp2_buf *buf) { + buf->pos = buf->last = buf->mark = buf->begin; +} + +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { + buf->begin = buf->pos = buf->last = buf->mark = begin; + buf->end = begin + len; +} + +static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, + nghttp2_mem *mem) { + int rv; + + *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); + if (*chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + (*chain)->next = NULL; + + rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem); + if (rv != 0) { + nghttp2_mem_free(mem, *chain); + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) { + nghttp2_buf_free(&chain->buf, mem); + nghttp2_mem_free(mem, chain); +} + +int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, + nghttp2_mem *mem) { + return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem); +} + +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset, nghttp2_mem *mem) { + return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset, + mem); +} + +int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t chunk_keep, size_t offset, + nghttp2_mem *mem) { + int rv; + nghttp2_buf_chain *chain; + + if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = buf_chain_new(&chain, chunk_length, mem); + if (rv != 0) { + return rv; + } + + bufs->mem = mem; + bufs->offset = offset; + + bufs->head = chain; + bufs->cur = bufs->head; + + nghttp2_buf_shift_right(&bufs->cur->buf, offset); + + bufs->chunk_length = chunk_length; + bufs->chunk_used = 1; + bufs->max_chunk = max_chunk; + bufs->chunk_keep = chunk_keep; + + return 0; +} + +int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) { + int rv; + nghttp2_buf_chain *chain; + + if (chunk_length < bufs->offset) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = buf_chain_new(&chain, chunk_length, bufs->mem); + if (rv != 0) { + return rv; + } + + nghttp2_bufs_free(bufs); + + bufs->head = chain; + bufs->cur = bufs->head; + + nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); + + bufs->chunk_length = chunk_length; + bufs->chunk_used = 1; + + return 0; +} + +void nghttp2_bufs_free(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain, *next_chain; + + if (bufs == NULL) { + return; + } + + for (chain = bufs->head; chain;) { + next_chain = chain->next; + + buf_chain_del(chain, bufs->mem); + + chain = next_chain; + } + + bufs->head = NULL; +} + +int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, + nghttp2_mem *mem) { + nghttp2_buf_chain *chain; + + chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); + if (chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + chain->next = NULL; + + nghttp2_buf_wrap_init(&chain->buf, begin, len); + + bufs->mem = mem; + bufs->offset = 0; + + bufs->head = chain; + bufs->cur = bufs->head; + + bufs->chunk_length = len; + bufs->chunk_used = 1; + bufs->max_chunk = 1; + bufs->chunk_keep = 1; + + return 0; +} + +int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec, + size_t veclen, nghttp2_mem *mem) { + size_t i = 0; + nghttp2_buf_chain *cur_chain; + nghttp2_buf_chain *head_chain; + nghttp2_buf_chain **dst_chain = &head_chain; + + if (veclen == 0) { + return nghttp2_bufs_wrap_init(bufs, NULL, 0, mem); + } + + head_chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain) * veclen); + if (head_chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < veclen; ++i) { + cur_chain = &head_chain[i]; + cur_chain->next = NULL; + nghttp2_buf_wrap_init(&cur_chain->buf, vec[i].base, vec[i].len); + + *dst_chain = cur_chain; + dst_chain = &cur_chain->next; + } + + bufs->mem = mem; + bufs->offset = 0; + + bufs->head = head_chain; + bufs->cur = bufs->head; + + /* We don't use chunk_length since no allocation is expected. */ + bufs->chunk_length = 0; + bufs->chunk_used = veclen; + bufs->max_chunk = veclen; + bufs->chunk_keep = veclen; + + return 0; +} + +void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) { + if (bufs == NULL) { + return; + } + + if (bufs->head) { + nghttp2_mem_free(bufs->mem, bufs->head); + } +} + +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + + for (ci = bufs->cur; ci; ci = ci->next) { + if (nghttp2_buf_len(&ci->buf) == 0) { + return; + } else { + bufs->cur = ci; + } + } +} + +size_t nghttp2_bufs_len(nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + size_t len; + + len = 0; + for (ci = bufs->head; ci; ci = ci->next) { + len += nghttp2_buf_len(&ci->buf); + } + + return len; +} + +static int bufs_alloc_chain(nghttp2_bufs *bufs) { + int rv; + nghttp2_buf_chain *chain; + + if (bufs->cur->next) { + bufs->cur = bufs->cur->next; + + return 0; + } + + if (bufs->max_chunk == bufs->chunk_used) { + return NGHTTP2_ERR_BUFFER_ERROR; + } + + rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem); + if (rv != 0) { + return rv; + } + + DEBUGF("new buffer %zu bytes allocated for bufs %p, used %zu\n", + bufs->chunk_length, bufs, bufs->chunk_used); + + ++bufs->chunk_used; + + bufs->cur->next = chain; + bufs->cur = chain; + + nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); + + return 0; +} + +int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) { + int rv; + size_t nwrite; + nghttp2_buf *buf; + const uint8_t *p; + + p = data; + + while (len) { + buf = &bufs->cur->buf; + + nwrite = nghttp2_min(nghttp2_buf_avail(buf), len); + if (nwrite == 0) { + rv = bufs_alloc_chain(bufs); + if (rv != 0) { + return rv; + } + continue; + } + + buf->last = nghttp2_cpymem(buf->last, p, nwrite); + p += nwrite; + len -= nwrite; + } + + return 0; +} + +static int bufs_ensure_addb(nghttp2_bufs *bufs) { + int rv; + nghttp2_buf *buf; + + buf = &bufs->cur->buf; + + if (nghttp2_buf_avail(buf) > 0) { + return 0; + } + + rv = bufs_alloc_chain(bufs); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ = b; + + return 0; +} + +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last = b; + + return 0; +} + +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ |= b; + + return 0; +} + +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last |= b; + + return 0; +} + +ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { + size_t len; + nghttp2_buf_chain *chain; + nghttp2_buf *buf; + uint8_t *res; + nghttp2_buf resbuf; + + len = 0; + + for (chain = bufs->head; chain; chain = chain->next) { + len += nghttp2_buf_len(&chain->buf); + } + + if (len == 0) { + res = NULL; + return 0; + } + + res = nghttp2_mem_malloc(bufs->mem, len); + if (res == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&resbuf, res, len); + + for (chain = bufs->head; chain; chain = chain->next) { + buf = &chain->buf; + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + } + + *out = res; + + return (ssize_t)len; +} + +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { + size_t len; + nghttp2_buf_chain *chain; + nghttp2_buf *buf; + nghttp2_buf resbuf; + + len = nghttp2_bufs_len(bufs); + + nghttp2_buf_wrap_init(&resbuf, out, len); + + for (chain = bufs->head; chain; chain = chain->next) { + buf = &chain->buf; + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + } + + return len; +} + +void nghttp2_bufs_reset(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain, *ci; + size_t k; + + k = bufs->chunk_keep; + + for (ci = bufs->head; ci; ci = ci->next) { + nghttp2_buf_reset(&ci->buf); + nghttp2_buf_shift_right(&ci->buf, bufs->offset); + + if (--k == 0) { + break; + } + } + + if (ci) { + chain = ci->next; + ci->next = NULL; + + for (ci = chain; ci;) { + chain = ci->next; + + buf_chain_del(ci, bufs->mem); + + ci = chain; + } + + bufs->chunk_used = bufs->chunk_keep; + } + + bufs->cur = bufs->head; +} + +int nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); } + +int nghttp2_bufs_next_present(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain; + + chain = bufs->cur->next; + + return chain && nghttp2_buf_len(&chain->buf); +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_buf.h b/Utilities/cmnghttp2/lib/nghttp2_buf.h new file mode 100644 index 00000000000..06cce67a11b --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_buf.h @@ -0,0 +1,412 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_BUF_H +#define NGHTTP2_BUF_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +typedef struct { + /* This points to the beginning of the buffer. The effective range + of buffer is [begin, end). */ + uint8_t *begin; + /* This points to the memory one byte beyond the end of the + buffer. */ + uint8_t *end; + /* The position indicator for effective start of the buffer. pos <= + last must be hold. */ + uint8_t *pos; + /* The position indicator for effective one beyond of the end of the + buffer. last <= end must be hold. */ + uint8_t *last; + /* Mark arbitrary position in buffer [begin, end) */ + uint8_t *mark; +} nghttp2_buf; + +#define nghttp2_buf_len(BUF) ((size_t)((BUF)->last - (BUF)->pos)) +#define nghttp2_buf_avail(BUF) ((size_t)((BUF)->end - (BUF)->last)) +#define nghttp2_buf_mark_avail(BUF) ((size_t)((BUF)->mark - (BUF)->last)) +#define nghttp2_buf_cap(BUF) ((size_t)((BUF)->end - (BUF)->begin)) + +#define nghttp2_buf_pos_offset(BUF) ((size_t)((BUF)->pos - (BUF)->begin)) +#define nghttp2_buf_last_offset(BUF) ((size_t)((BUF)->last - (BUF)->begin)) + +#define nghttp2_buf_shift_right(BUF, AMT) \ + do { \ + (BUF)->pos += AMT; \ + (BUF)->last += AMT; \ + } while (0) + +#define nghttp2_buf_shift_left(BUF, AMT) \ + do { \ + (BUF)->pos -= AMT; \ + (BUF)->last -= AMT; \ + } while (0) + +/* + * Initializes the |buf|. No memory is allocated in this function. Use + * nghttp2_buf_reserve() to allocate memory. + */ +void nghttp2_buf_init(nghttp2_buf *buf); + +/* + * Initializes the |buf| and allocates at least |initial| bytes of + * memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem); + +/* + * Frees buffer in |buf|. + */ +void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); + +/* + * Extends buffer so that nghttp2_buf_cap() returns at least + * |new_cap|. If extensions took place, buffer pointers in |buf| will + * change. + * + * This function returns 0 if it succeeds, or one of the followings + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem); + +/* + * Resets pos, last, mark member of |buf| to buf->begin. + */ +void nghttp2_buf_reset(nghttp2_buf *buf); + +/* + * Initializes |buf| using supplied buffer |begin| of length + * |len|. Semantically, the application should not call *_reserve() or + * nghttp2_free() functions for |buf|. + */ +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len); + +struct nghttp2_buf_chain; + +typedef struct nghttp2_buf_chain nghttp2_buf_chain; + +/* Chains 2 buffers */ +struct nghttp2_buf_chain { + /* Points to the subsequent buffer. NULL if there is no such + buffer. */ + nghttp2_buf_chain *next; + nghttp2_buf buf; +}; + +typedef struct { + /* Points to the first buffer */ + nghttp2_buf_chain *head; + /* Buffer pointer where write occurs. */ + nghttp2_buf_chain *cur; + /* Memory allocator */ + nghttp2_mem *mem; + /* The buffer capacity of each buf. This field may be 0 if + nghttp2_bufs is initialized by nghttp2_bufs_wrap_init* family + functions. */ + size_t chunk_length; + /* The maximum number of nghttp2_buf_chain */ + size_t max_chunk; + /* The number of nghttp2_buf_chain allocated */ + size_t chunk_used; + /* The number of nghttp2_buf_chain to keep on reset */ + size_t chunk_keep; + /* pos offset from begin in each buffers. On initialization and + reset, buf->pos and buf->last are positioned at buf->begin + + offset. */ + size_t offset; +} nghttp2_bufs; + +/* + * This is the same as calling nghttp2_bufs_init2 with the given + * arguments and offset = 0. + */ +int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, + nghttp2_mem *mem); + +/* + * This is the same as calling nghttp2_bufs_init3 with the given + * arguments and chunk_keep = max_chunk. + */ +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset, nghttp2_mem *mem); + +/* + * Initializes |bufs|. Each buffer size is given in the + * |chunk_length|. The maximum number of buffers is given in the + * |max_chunk|. On reset, first |chunk_keep| buffers are kept and + * remaining buffers are deleted. Each buffer will have bufs->pos and + * bufs->last shifted to left by |offset| bytes on creation and reset. + * + * This function allocates first buffer. bufs->head and bufs->cur + * will point to the first buffer after this call. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * chunk_keep is 0; or max_chunk < chunk_keep; or offset is too + * long. + */ +int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t chunk_keep, size_t offset, + nghttp2_mem *mem); + +/* + * Frees any related resources to the |bufs|. + */ +void nghttp2_bufs_free(nghttp2_bufs *bufs); + +/* + * Initializes |bufs| using supplied buffer |begin| of length |len|. + * The first buffer bufs->head uses buffer |begin|. The buffer size + * is fixed and no extra chunk buffer is allocated. In other + * words, max_chunk = chunk_keep = 1. To free the resource allocated + * for |bufs|, use nghttp2_bufs_wrap_free(). + * + * Don't use the function which performs allocation, such as + * nghttp2_bufs_realloc(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, + nghttp2_mem *mem); + +/* + * Initializes |bufs| using supplied |veclen| size of buf vector + * |vec|. The number of buffers is fixed and no extra chunk buffer is + * allocated. In other words, max_chunk = chunk_keep = |in_len|. To + * free the resource allocated for |bufs|, use + * nghttp2_bufs_wrap_free(). + * + * Don't use the function which performs allocation, such as + * nghttp2_bufs_realloc(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec, + size_t veclen, nghttp2_mem *mem); + +/* + * Frees any related resource to the |bufs|. This function does not + * free supplied buffer provided in nghttp2_bufs_wrap_init(). + */ +void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs); + +/* + * Reallocates internal buffer using |chunk_length|. The max_chunk, + * chunk_keep and offset do not change. After successful allocation + * of new buffer, previous buffers are deallocated without copying + * anything into new buffers. chunk_used is reset to 1. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * chunk_length < offset + */ +int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length); + +/* + * Appends the |data| of length |len| to the |bufs|. The write starts + * at bufs->cur->buf.last. A new buffers will be allocated to store + * all data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len); + +/* + * Appends a single byte |b| to the |bufs|. The write starts at + * bufs->cur->buf.last. A new buffers will be allocated to store all + * data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b); + +/* + * Behaves like nghttp2_bufs_addb(), but this does not update + * buf->last pointer. + */ +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_addb(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last++ = B; \ + } while (0) + +#define nghttp2_bufs_fast_addb_hold(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last = B; \ + } while (0) + +/* + * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers + * will be allocated if necessary. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b); + +/* + * Behaves like nghttp2_bufs_orb(), but does not update buf->last + * pointer. + */ +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_orb(BUFS, B) \ + do { \ + uint8_t **p = &(BUFS)->cur->buf.last; \ + **p = (uint8_t)(**p | (B)); \ + ++(*p); \ + } while (0) + +#define nghttp2_bufs_fast_orb_hold(BUFS, B) \ + do { \ + uint8_t *p = (BUFS)->cur->buf.last; \ + *p = (uint8_t)(*p | (B)); \ + } while (0) + +/* + * Copies all data stored in |bufs| to the contiguous buffer. This + * function allocates the contiguous memory to store all data in + * |bufs| and assigns it to |*out|. + * + * The contents of |bufs| is left unchanged. + * + * This function returns the length of copied data and assigns the + * pointer to copied data to |*out| if it succeeds, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); + +/* + * Copies all data stored in |bufs| to |out|. This function assumes + * that the buffer space pointed by |out| has at least + * nghttp2_bufs(bufs) bytes. + * + * The contents of |bufs| is left unchanged. + * + * This function returns the length of copied data. + */ +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out); + +/* + * Resets |bufs| and makes the buffers empty. + */ +void nghttp2_bufs_reset(nghttp2_bufs *bufs); + +/* + * Moves bufs->cur to bufs->cur->next. If resulting bufs->cur is + * NULL, this function allocates new buffers and bufs->cur points to + * it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_advance(nghttp2_bufs *bufs); + +/* Sets bufs->cur to bufs->head */ +#define nghttp2_bufs_rewind(BUFS) \ + do { \ + (BUFS)->cur = (BUFS)->head; \ + } while (0) + +/* + * Move bufs->cur, from the current position, using next member, to + * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf + * which satisfies nghttp2_buf_len(buf) == 0. If + * nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL, + * bufs->cur is unchanged. + */ +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); + +/* + * Returns nonzero if bufs->cur->next is not empty. + */ +int nghttp2_bufs_next_present(nghttp2_bufs *bufs); + +#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf) + +/* + * Returns the total buffer length of |bufs|. + */ +size_t nghttp2_bufs_len(nghttp2_bufs *bufs); + +#endif /* NGHTTP2_BUF_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_callbacks.c b/Utilities/cmnghttp2/lib/nghttp2_callbacks.c new file mode 100644 index 00000000000..3c38214859b --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_callbacks.c @@ -0,0 +1,175 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_callbacks.h" + +#include + +int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) { + *callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks)); + + if (*callbacks_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) { + free(callbacks); +} + +void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) { + cbs->send_callback = send_callback; +} + +void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) { + cbs->recv_callback = recv_callback; +} + +void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback) { + cbs->on_frame_recv_callback = on_frame_recv_callback; +} + +void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) { + cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; +} + +void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) { + cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback; +} + +void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback) { + cbs->before_frame_send_callback = before_frame_send_callback; +} + +void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback) { + cbs->on_frame_send_callback = on_frame_send_callback; +} + +void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback) { + cbs->on_frame_not_send_callback = on_frame_not_send_callback; +} + +void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback) { + cbs->on_stream_close_callback = on_stream_close_callback; +} + +void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback) { + cbs->on_begin_headers_callback = on_begin_headers_callback; +} + +void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback) { + cbs->on_header_callback = on_header_callback; +} + +void nghttp2_session_callbacks_set_on_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback2 on_header_callback2) { + cbs->on_header_callback2 = on_header_callback2; +} + +void nghttp2_session_callbacks_set_on_invalid_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback on_invalid_header_callback) { + cbs->on_invalid_header_callback = on_invalid_header_callback; +} + +void nghttp2_session_callbacks_set_on_invalid_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback2 on_invalid_header_callback2) { + cbs->on_invalid_header_callback2 = on_invalid_header_callback2; +} + +void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback) { + cbs->select_padding_callback = select_padding_callback; +} + +void nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback) { + cbs->read_length_callback = data_source_read_length_callback; +} + +void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback) { + cbs->on_begin_frame_callback = on_begin_frame_callback; +} + +void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback) { + cbs->send_data_callback = send_data_callback; +} + +void nghttp2_session_callbacks_set_pack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_pack_extension_callback pack_extension_callback) { + cbs->pack_extension_callback = pack_extension_callback; +} + +void nghttp2_session_callbacks_set_unpack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_unpack_extension_callback unpack_extension_callback) { + cbs->unpack_extension_callback = unpack_extension_callback; +} + +void nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback) { + cbs->on_extension_chunk_recv_callback = on_extension_chunk_recv_callback; +} + +void nghttp2_session_callbacks_set_error_callback( + nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) { + cbs->error_callback = error_callback; +} + +void nghttp2_session_callbacks_set_error_callback2( + nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) { + cbs->error_callback2 = error_callback2; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_callbacks.h b/Utilities/cmnghttp2/lib/nghttp2_callbacks.h new file mode 100644 index 00000000000..61e51fa5363 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_callbacks.h @@ -0,0 +1,125 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_CALLBACKS_H +#define NGHTTP2_CALLBACKS_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * Callback functions. + */ +struct nghttp2_session_callbacks { + /** + * Callback function invoked when the session wants to send data to + * the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_send()` to serialize + * data to transmit. + */ + nghttp2_send_callback send_callback; + /** + * Callback function invoked when the session wants to receive data + * from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ + nghttp2_recv_callback recv_callback; + /** + * Callback function invoked by `nghttp2_session_recv()` when a + * frame is received. + */ + nghttp2_on_frame_recv_callback on_frame_recv_callback; + /** + * Callback function invoked by `nghttp2_session_recv()` when an + * invalid non-DATA frame is received. + */ + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback; + /** + * Callback function invoked when a chunk of data in DATA frame is + * received. + */ + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback; + /** + * Callback function invoked before a non-DATA frame is sent. + */ + nghttp2_before_frame_send_callback before_frame_send_callback; + /** + * Callback function invoked after a frame is sent. + */ + nghttp2_on_frame_send_callback on_frame_send_callback; + /** + * The callback function invoked when a non-DATA frame is not sent + * because of an error. + */ + nghttp2_on_frame_not_send_callback on_frame_not_send_callback; + /** + * Callback function invoked when the stream is closed. + */ + nghttp2_on_stream_close_callback on_stream_close_callback; + /** + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. + */ + nghttp2_on_begin_headers_callback on_begin_headers_callback; + /** + * Callback function invoked when a header name/value pair is + * received. + */ + nghttp2_on_header_callback on_header_callback; + nghttp2_on_header_callback2 on_header_callback2; + /** + * Callback function invoked when a invalid header name/value pair + * is received which is silently ignored if these callbacks are not + * set. + */ + nghttp2_on_invalid_header_callback on_invalid_header_callback; + nghttp2_on_invalid_header_callback2 on_invalid_header_callback2; + /** + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the given + * frame. + */ + nghttp2_select_padding_callback select_padding_callback; + /** + * The callback function used to determine the length allowed in + * `nghttp2_data_source_read_callback()` + */ + nghttp2_data_source_read_length_callback read_length_callback; + /** + * Sets callback function invoked when a frame header is received. + */ + nghttp2_on_begin_frame_callback on_begin_frame_callback; + nghttp2_send_data_callback send_data_callback; + nghttp2_pack_extension_callback pack_extension_callback; + nghttp2_unpack_extension_callback unpack_extension_callback; + nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback; + nghttp2_error_callback error_callback; + nghttp2_error_callback2 error_callback2; +}; + +#endif /* NGHTTP2_CALLBACKS_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_debug.c b/Utilities/cmnghttp2/lib/nghttp2_debug.c new file mode 100644 index 00000000000..cb2779700bd --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_debug.c @@ -0,0 +1,60 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_debug.h" + +#include + +#ifdef DEBUGBUILD + +static void nghttp2_default_debug_vfprintf_callback(const char *fmt, + va_list args) { + vfprintf(stderr, fmt, args); +} + +static nghttp2_debug_vprintf_callback static_debug_vprintf_callback = + nghttp2_default_debug_vfprintf_callback; + +void nghttp2_debug_vprintf(const char *format, ...) { + if (static_debug_vprintf_callback) { + va_list args; + va_start(args, format); + static_debug_vprintf_callback(format, args); + va_end(args); + } +} + +void nghttp2_set_debug_vprintf_callback( + nghttp2_debug_vprintf_callback debug_vprintf_callback) { + static_debug_vprintf_callback = debug_vprintf_callback; +} + +#else /* !DEBUGBUILD */ + +void nghttp2_set_debug_vprintf_callback( + nghttp2_debug_vprintf_callback debug_vprintf_callback) { + (void)debug_vprintf_callback; +} + +#endif /* !DEBUGBUILD */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_debug.h b/Utilities/cmnghttp2/lib/nghttp2_debug.h new file mode 100644 index 00000000000..cbb4dd57547 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_debug.h @@ -0,0 +1,43 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_DEBUG_H +#define NGHTTP2_DEBUG_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#ifdef DEBUGBUILD +# define DEBUGF(...) nghttp2_debug_vprintf(__VA_ARGS__) +void nghttp2_debug_vprintf(const char *format, ...); +#else +# define DEBUGF(...) \ + do { \ + } while (0) +#endif + +#endif /* NGHTTP2_DEBUG_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_frame.c b/Utilities/cmnghttp2/lib/nghttp2_frame.c new file mode 100644 index 00000000000..4821de40885 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_frame.c @@ -0,0 +1,1134 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_frame.h" + +#include +#include +#include +#include + +#include "nghttp2_helper.h" +#include "nghttp2_net.h" +#include "nghttp2_priority_spec.h" +#include "nghttp2_debug.h" + +void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) { + nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8)); + buf[3] = hd->type; + buf[4] = hd->flags; + nghttp2_put_uint32be(&buf[5], (uint32_t)hd->stream_id); + /* ignore hd->reserved for now */ +} + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) { + hd->length = nghttp2_get_uint32(&buf[0]) >> 8; + hd->type = buf[3]; + hd->flags = buf[4]; + hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK; + hd->reserved = 0; +} + +void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, + uint8_t flags, int32_t stream_id) { + hd->length = length; + hd->type = type; + hd->flags = flags; + hd->stream_id = stream_id; + hd->reserved = 0; +} + +void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, + int32_t stream_id, nghttp2_headers_category cat, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva, size_t nvlen) { + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id); + frame->padlen = 0; + frame->nva = nva; + frame->nvlen = nvlen; + frame->cat = cat; + + if (pri_spec) { + frame->pri_spec = *pri_spec; + } else { + nghttp2_priority_spec_default_init(&frame->pri_spec); + } +} + +void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) { + nghttp2_nv_array_del(frame->nva, mem); +} + +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY, + NGHTTP2_FLAG_NONE, stream_id); + frame->pri_spec = *pri_spec; +} + +void nghttp2_frame_priority_free(nghttp2_priority *frame) { (void)frame; } + +void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, + uint32_t error_code) { + nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, + stream_id); + frame->error_code = error_code; +} + +void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) { (void)frame; } + +void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, + nghttp2_settings_entry *iv, size_t niv) { + nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, + NGHTTP2_SETTINGS, flags, 0); + frame->niv = niv; + frame->iv = iv; +} + +void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) { + nghttp2_mem_free(mem, frame->iv); +} + +void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, + int32_t stream_id, + int32_t promised_stream_id, + nghttp2_nv *nva, size_t nvlen) { + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id); + frame->padlen = 0; + frame->nva = nva; + frame->nvlen = nvlen; + frame->promised_stream_id = promised_stream_id; + frame->reserved = 0; +} + +void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, + nghttp2_mem *mem) { + nghttp2_nv_array_del(frame->nva, mem); +} + +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opaque_data) { + nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0); + if (opaque_data) { + memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data)); + } else { + memset(frame->opaque_data, 0, sizeof(frame->opaque_data)); + } +} + +void nghttp2_frame_ping_free(nghttp2_ping *frame) { (void)frame; } + +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + uint32_t error_code, uint8_t *opaque_data, + size_t opaque_data_len) { + nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY, + NGHTTP2_FLAG_NONE, 0); + frame->last_stream_id = last_stream_id; + frame->error_code = error_code; + frame->opaque_data = opaque_data; + frame->opaque_data_len = opaque_data_len; + frame->reserved = 0; +} + +void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) { + nghttp2_mem_free(mem, frame->opaque_data); +} + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, int32_t stream_id, + int32_t window_size_increment) { + nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id); + frame->window_size_increment = window_size_increment; + frame->reserved = 0; +} + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame) { + (void)frame; +} + +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) { + /* We have iframe->padlen == 0, but iframe->frame.hd.flags may have + NGHTTP2_FLAG_PADDED set. This happens when receiving + CONTINUATION frame, since we don't reset flags after HEADERS was + received. */ + if (padlen == 0) { + return 0; + } + return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); +} + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id) { + /* At this moment, the length of DATA frame is unknown */ + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id); + frame->padlen = 0; +} + +void nghttp2_frame_data_free(nghttp2_data *frame) { (void)frame; } + +void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type, + uint8_t flags, int32_t stream_id, + void *payload) { + nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id); + frame->payload = payload; +} + +void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; } + +void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id, + uint8_t *origin, size_t origin_len, + uint8_t *field_value, size_t field_value_len) { + nghttp2_ext_altsvc *altsvc; + + nghttp2_frame_hd_init(&frame->hd, 2 + origin_len + field_value_len, + NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, stream_id); + + altsvc = frame->payload; + altsvc->origin = origin; + altsvc->origin_len = origin_len; + altsvc->field_value = field_value; + altsvc->field_value_len = field_value_len; +} + +void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) { + nghttp2_ext_altsvc *altsvc; + + altsvc = frame->payload; + if (altsvc == NULL) { + return; + } + /* We use the same buffer for altsvc->origin and + altsvc->field_value. */ + nghttp2_mem_free(mem, altsvc->origin); +} + +void nghttp2_frame_origin_init(nghttp2_extension *frame, + nghttp2_origin_entry *ov, size_t nov) { + nghttp2_ext_origin *origin; + size_t payloadlen = 0; + size_t i; + + for (i = 0; i < nov; ++i) { + payloadlen += 2 + ov[i].origin_len; + } + + nghttp2_frame_hd_init(&frame->hd, payloadlen, NGHTTP2_ORIGIN, + NGHTTP2_FLAG_NONE, 0); + + origin = frame->payload; + origin->ov = ov; + origin->nov = nov; +} + +void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) { + nghttp2_ext_origin *origin; + + origin = frame->payload; + if (origin == NULL) { + return; + } + /* We use the same buffer for all resources pointed by the field of + origin directly or indirectly. */ + nghttp2_mem_free(mem, origin->ov); +} + +size_t nghttp2_frame_priority_len(uint8_t flags) { + if (flags & NGHTTP2_FLAG_PRIORITY) { + return NGHTTP2_PRIORITY_SPECLEN; + } + + return 0; +} + +size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) { + return nghttp2_frame_priority_len(frame->hd.flags); +} + +/* + * Call this function after payload was serialized, but not before + * changing buf->pos and serializing frame header. + * + * This function assumes bufs->cur points to the last buf chain of the + * frame(s). + * + * This function serializes frame header for HEADERS/PUSH_PROMISE and + * handles their successive CONTINUATION frames. + * + * We don't process any padding here. + */ +static int frame_pack_headers_shared(nghttp2_bufs *bufs, + nghttp2_frame_hd *frame_hd) { + nghttp2_buf *buf; + nghttp2_buf_chain *ci, *ce; + nghttp2_frame_hd hd; + + buf = &bufs->head->buf; + + hd = *frame_hd; + hd.length = nghttp2_buf_len(buf); + + DEBUGF("send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length); + + /* We have multiple frame buffers, which means one or more + CONTINUATION frame is involved. Remove END_HEADERS flag from the + first frame. */ + if (bufs->head != bufs->cur) { + hd.flags = (uint8_t)(hd.flags & ~NGHTTP2_FLAG_END_HEADERS); + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + + if (bufs->head != bufs->cur) { + /* 2nd and later frames are CONTINUATION frames. */ + hd.type = NGHTTP2_CONTINUATION; + /* We don't have no flags except for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_NONE; + + ce = bufs->cur; + + for (ci = bufs->head->next; ci != ce; ci = ci->next) { + buf = &ci->buf; + + hd.length = nghttp2_buf_len(buf); + + DEBUGF("send: int CONTINUATION, payloadlen=%zu\n", hd.length); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + buf = &ci->buf; + hd.length = nghttp2_buf_len(buf); + /* Set END_HEADERS flag for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_END_HEADERS; + + DEBUGF("send: last CONTINUATION, payloadlen=%zu\n", hd.length); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + return 0; +} + +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, + nghttp2_hd_deflater *deflater) { + size_t nv_offset; + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + nv_offset = nghttp2_frame_headers_payload_nv_offset(frame); + + buf = &bufs->cur->buf; + + buf->pos += nv_offset; + buf->last = buf->pos; + + /* This call will adjust buf->last to the correct position */ + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + rv = NGHTTP2_ERR_HEADER_COMP; + } + + buf->pos -= nv_offset; + + if (rv != 0) { + return rv; + } + + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec); + } + + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); + + return frame_pack_headers_shared(bufs, &frame->hd); +} + +void nghttp2_frame_pack_priority_spec(uint8_t *buf, + const nghttp2_priority_spec *pri_spec) { + nghttp2_put_uint32be(buf, (uint32_t)pri_spec->stream_id); + if (pri_spec->exclusive) { + buf[0] |= 0x80; + } + buf[4] = (uint8_t)(pri_spec->weight - 1); +} + +void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, + const uint8_t *payload) { + int32_t dep_stream_id; + uint8_t exclusive; + int32_t weight; + + dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + exclusive = (payload[0] & 0x80) > 0; + weight = payload[4] + 1; + + nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive); +} + +int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + const uint8_t *payload) { + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); + } else { + nghttp2_priority_spec_default_init(&frame->pri_spec); + } + + frame->nva = NULL; + frame->nvlen = 0; + + return 0; +} + +int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec); + + buf->last += NGHTTP2_PRIORITY_SPECLEN; + + return 0; +} + +void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + const uint8_t *payload) { + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); +} + +int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 4); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; + + return 0; +} + +void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, + const uint8_t *payload) { + frame->error_code = nghttp2_get_uint32(payload); +} + +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + if (nghttp2_buf_avail(buf) < frame->hd.length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + buf->last += + nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv); + + return 0; +} + +size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, + const nghttp2_settings_entry *iv, + size_t niv) { + size_t i; + for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { + nghttp2_put_uint16be(buf, (uint16_t)iv[i].settings_id); + nghttp2_put_uint32be(buf + 2, iv[i].value); + } + return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv; +} + +void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, + nghttp2_settings_entry *iv, + size_t niv) { + frame->iv = iv; + frame->niv = niv; +} + +void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, + const uint8_t *payload) { + iv->settings_id = nghttp2_get_uint16(&payload[0]); + iv->value = nghttp2_get_uint32(&payload[2]); +} + +int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, + size_t *niv_ptr, + const uint8_t *payload, + size_t payloadlen, + nghttp2_mem *mem) { + size_t i; + + *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; + + if (*niv_ptr == 0) { + *iv_ptr = NULL; + + return 0; + } + + *iv_ptr = + nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry)); + + if (*iv_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < *niv_ptr; ++i) { + size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; + nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]); + } + + return 0; +} + +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater) { + size_t nv_offset = 4; + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->cur->buf; + + buf->pos += nv_offset; + buf->last = buf->pos; + + /* This call will adjust buf->last to the correct position */ + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + rv = NGHTTP2_ERR_HEADER_COMP; + } + + buf->pos -= nv_offset; + + if (rv != 0) { + return rv; + } + + nghttp2_put_uint32be(buf->pos, (uint32_t)frame->promised_stream_id); + + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); + + return frame_pack_headers_shared(bufs, &frame->hd); +} + +int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, + const uint8_t *payload) { + frame->promised_stream_id = + nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->nva = NULL; + frame->nvlen = 0; + return 0; +} + +int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 8); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + buf->last = + nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); + + return 0; +} + +void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, + const uint8_t *payload) { + memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); +} + +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) { + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, (uint32_t)frame->last_stream_id); + buf->last += 4; + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; + + rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + if (rv != 0) { + return rv; + } + + return 0; +} + +void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, + const uint8_t *payload, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen) { + frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->error_code = nghttp2_get_uint32(payload + 4); + + frame->opaque_data = var_gift_payload; + frame->opaque_data_len = var_gift_payloadlen; +} + +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem) { + uint8_t *var_gift_payload; + size_t var_gift_payloadlen; + + if (payloadlen > 8) { + var_gift_payloadlen = payloadlen - 8; + } else { + var_gift_payloadlen = 0; + } + + payloadlen -= var_gift_payloadlen; + + if (!var_gift_payloadlen) { + var_gift_payload = NULL; + } else { + var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen); + + if (var_gift_payload == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + memcpy(var_gift_payload, payload + 8, var_gift_payloadlen); + } + + nghttp2_frame_unpack_goaway_payload(frame, payload, var_gift_payload, + var_gift_payloadlen); + + return 0; +} + +int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 4); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment); + buf->last += 4; + + return 0; +} + +void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + const uint8_t *payload) { + frame->window_size_increment = + nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; +} + +int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) { + int rv; + nghttp2_buf *buf; + nghttp2_ext_altsvc *altsvc; + + /* This is required with --disable-assert. */ + (void)rv; + + altsvc = frame->payload; + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= + 2 + altsvc->origin_len + altsvc->field_value_len); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint16be(buf->last, (uint16_t)altsvc->origin_len); + buf->last += 2; + + rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len); + + assert(rv == 0); + + rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len); + + assert(rv == 0); + + return 0; +} + +void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, + size_t origin_len, uint8_t *payload, + size_t payloadlen) { + nghttp2_ext_altsvc *altsvc; + uint8_t *p; + + altsvc = frame->payload; + p = payload; + + altsvc->origin = p; + + p += origin_len; + + altsvc->origin_len = origin_len; + + altsvc->field_value = p; + altsvc->field_value_len = (size_t)(payload + payloadlen - p); +} + +int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem) { + uint8_t *buf; + size_t origin_len; + + if (payloadlen < 2) { + return NGHTTP2_FRAME_SIZE_ERROR; + } + + origin_len = nghttp2_get_uint16(payload); + + buf = nghttp2_mem_malloc(mem, payloadlen - 2); + if (!buf) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_cpymem(buf, payload + 2, payloadlen - 2); + + nghttp2_frame_unpack_altsvc_payload(frame, origin_len, buf, payloadlen - 2); + + return 0; +} + +int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) { + nghttp2_buf *buf; + nghttp2_ext_origin *origin; + nghttp2_origin_entry *orig; + size_t i; + + origin = frame->payload; + + buf = &bufs->head->buf; + + if (nghttp2_buf_avail(buf) < frame->hd.length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + for (i = 0; i < origin->nov; ++i) { + orig = &origin->ov[i]; + nghttp2_put_uint16be(buf->last, (uint16_t)orig->origin_len); + buf->last += 2; + buf->last = nghttp2_cpymem(buf->last, orig->origin, orig->origin_len); + } + + assert(nghttp2_buf_len(buf) == NGHTTP2_FRAME_HDLEN + frame->hd.length); + + return 0; +} + +int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem) { + nghttp2_ext_origin *origin; + const uint8_t *p, *end; + uint8_t *dst; + size_t originlen; + nghttp2_origin_entry *ov; + size_t nov = 0; + size_t len = 0; + + origin = frame->payload; + p = payload; + end = p + payloadlen; + + for (; p != end;) { + if (end - p < 2) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + originlen = nghttp2_get_uint16(p); + p += 2; + if (originlen == 0) { + continue; + } + if (originlen > (size_t)(end - p)) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + p += originlen; + /* 1 for terminal NULL */ + len += originlen + 1; + ++nov; + } + + if (nov == 0) { + origin->ov = NULL; + origin->nov = 0; + + return 0; + } + + len += nov * sizeof(nghttp2_origin_entry); + + ov = nghttp2_mem_malloc(mem, len); + if (ov == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + origin->ov = ov; + origin->nov = nov; + + dst = (uint8_t *)ov + nov * sizeof(nghttp2_origin_entry); + p = payload; + + for (; p != end;) { + originlen = nghttp2_get_uint16(p); + p += 2; + if (originlen == 0) { + continue; + } + ov->origin = dst; + ov->origin_len = originlen; + dst = nghttp2_cpymem(dst, p, originlen); + *dst++ = '\0'; + p += originlen; + ++ov; + } + + return 0; +} + +nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem) { + nghttp2_settings_entry *iv_copy; + size_t len = niv * sizeof(nghttp2_settings_entry); + + if (len == 0) { + return NULL; + } + + iv_copy = nghttp2_mem_malloc(mem, len); + + if (iv_copy == NULL) { + return NULL; + } + + memcpy(iv_copy, iv, len); + + return iv_copy; +} + +int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { + return a->namelen == b->namelen && a->valuelen == b->valuelen && + memcmp(a->name, b->name, a->namelen) == 0 && + memcmp(a->value, b->value, a->valuelen) == 0; +} + +void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) { + nghttp2_mem_free(mem, nva); +} + +static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b, + size_t blen) { + int rv; + + if (alen == blen) { + return memcmp(a, b, alen); + } + + if (alen < blen) { + rv = memcmp(a, b, alen); + + if (rv == 0) { + return -1; + } + + return rv; + } + + rv = memcmp(a, b, blen); + + if (rv == 0) { + return 1; + } + + return rv; +} + +int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) { + return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen); +} + +static int nv_compar(const void *lhs, const void *rhs) { + const nghttp2_nv *a = (const nghttp2_nv *)lhs; + const nghttp2_nv *b = (const nghttp2_nv *)rhs; + int rv; + + rv = bytes_compar(a->name, a->namelen, b->name, b->namelen); + + if (rv == 0) { + return bytes_compar(a->value, a->valuelen, b->value, b->valuelen); + } + + return rv; +} + +void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { + qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar); +} + +int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem) { + size_t i; + uint8_t *data = NULL; + size_t buflen = 0; + nghttp2_nv *p; + + if (nvlen == 0) { + *nva_ptr = NULL; + + return 0; + } + + for (i = 0; i < nvlen; ++i) { + /* + 1 for null-termination */ + if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) { + buflen += nva[i].namelen + 1; + } + if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) { + buflen += nva[i].valuelen + 1; + } + } + + buflen += sizeof(nghttp2_nv) * nvlen; + + *nva_ptr = nghttp2_mem_malloc(mem, buflen); + + if (*nva_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + p = *nva_ptr; + data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; + + for (i = 0; i < nvlen; ++i) { + p->flags = nva[i].flags; + + if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) { + p->name = nva[i].name; + p->namelen = nva[i].namelen; + } else { + if (nva[i].namelen) { + memcpy(data, nva[i].name, nva[i].namelen); + } + p->name = data; + p->namelen = nva[i].namelen; + data[p->namelen] = '\0'; + nghttp2_downcase(p->name, p->namelen); + data += nva[i].namelen + 1; + } + + if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) { + p->value = nva[i].value; + p->valuelen = nva[i].valuelen; + } else { + if (nva[i].valuelen) { + memcpy(data, nva[i].value, nva[i].valuelen); + } + p->value = data; + p->valuelen = nva[i].valuelen; + data[p->valuelen] = '\0'; + data += nva[i].valuelen + 1; + } + + ++p; + } + return 0; +} + +int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { + size_t i; + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + if (iv[i].value != 0 && iv[i].value != 1) { + return 0; + } + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN || + iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + break; + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + if (iv[i].value != 0 && iv[i].value != 1) { + return 0; + } + break; + } + } + return 1; +} + +static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) { + size_t trail_padlen; + size_t newlen; + + DEBUGF("send: padlen=%zu, shift left 1 bytes\n", padlen); + + memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN); + + --buf->pos; + + buf->pos[4] |= NGHTTP2_FLAG_PADDED; + + newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen; + nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3])); + + if (framehd_only) { + return; + } + + trail_padlen = padlen - 1; + buf->pos[NGHTTP2_FRAME_HDLEN] = (uint8_t)trail_padlen; + + /* zero out padding */ + memset(buf->last, 0, trail_padlen); + /* extend buffers trail_padlen bytes, since we ate previous padlen - + trail_padlen byte(s) */ + buf->last += trail_padlen; +} + +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, int framehd_only) { + nghttp2_buf *buf; + + if (padlen == 0) { + DEBUGF("send: padlen = 0, nothing to do\n"); + + return 0; + } + + /* + * We have arranged bufs like this: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * + * We arranged padding so that it is included in the first frame + * completely. For padded frame, we are going to adjust buf->pos of + * frame which includes padding and serialize (memmove) frame header + * in the correct position. Also extends buf->last to include + * padding. + */ + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= padlen - 1); + + frame_set_pad(buf, padlen, framehd_only); + + hd->length += padlen; + hd->flags |= NGHTTP2_FLAG_PADDED; + + DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen); + + return 0; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_frame.h b/Utilities/cmnghttp2/lib/nghttp2_frame.h new file mode 100644 index 00000000000..615bbf31f5d --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_frame.h @@ -0,0 +1,624 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_FRAME_H +#define NGHTTP2_FRAME_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_hd.h" +#include "nghttp2_buf.h" + +#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) +#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1) +#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1) +#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1) +#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1) + +/* The number of bytes of frame header. */ +#define NGHTTP2_FRAME_HDLEN 9 + +#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1) +#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14) + +#define NGHTTP2_MAX_PAYLOADLEN 16384 +/* The one frame buffer length for tranmission. We may use several of + them to support CONTINUATION. To account for Pad Length field, we + allocate extra 1 byte, which saves extra large memcopying. */ +#define NGHTTP2_FRAMEBUF_CHUNKLEN \ + (NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN) + +/* The default length of DATA frame payload. */ +#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN + +/* Maximum headers block size to send, calculated using + nghttp2_hd_deflate_bound(). This is the default value, and can be + overridden by nghttp2_option_set_max_send_header_block_size(). */ +#define NGHTTP2_MAX_HEADERSLEN 65536 + +/* The number of bytes for each SETTINGS entry */ +#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 6 + +/* Length of priority related fields in HEADERS/PRIORITY frames */ +#define NGHTTP2_PRIORITY_SPECLEN 5 + +/* Maximum length of padding in bytes. */ +#define NGHTTP2_MAX_PADLEN 256 + +/* Union of extension frame payload */ +typedef union { + nghttp2_ext_altsvc altsvc; + nghttp2_ext_origin origin; +} nghttp2_ext_frame_payload; + +void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf); + +/** + * Initializes frame header |hd| with given parameters. Reserved bit + * is set to 0. + */ +void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, + uint8_t flags, int32_t stream_id); + +/** + * Returns the number of priority field depending on the |flags|. If + * |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor + * NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0. + */ +size_t nghttp2_frame_priority_len(uint8_t flags); + +/** + * Packs the |pri_spec| in |buf|. This function assumes |buf| has + * enough space for serialization. + */ +void nghttp2_frame_pack_priority_spec(uint8_t *buf, + const nghttp2_priority_spec *pri_spec); + +/** + * Unpacks the priority specification from payload |payload| of length + * |payloadlen| to |pri_spec|. The |flags| is used to determine what + * kind of priority specification is in |payload|. This function + * assumes the |payload| contains whole priority specification. + */ +void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, + const uint8_t *payload); + +/* + * Returns the offset from the HEADERS frame payload where the + * compressed header block starts. The frame payload does not include + * frame header. + */ +size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); + +/* + * Packs HEADERS frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * frame->hd.length is assigned after length is determined during + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The deflate operation failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, + nghttp2_hd_deflater *deflater); + +/* + * Unpacks HEADERS frame byte sequence into |frame|. This function + * only unapcks bytes that come before name/value header block and + * after possible Pad Length field. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + const uint8_t *payload); + +/* + * Packs PRIORITY frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame); + +/* + * Unpacks PRIORITY wire format into |frame|. + */ +void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + const uint8_t *payload); + +/* + * Packs RST_STREAM frame |frame| in wire frame format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame); + +/* + * Unpacks RST_STREAM frame byte sequence into |frame|. + */ +void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, + const uint8_t *payload); + +/* + * Packs SETTINGS frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. + */ +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame); + +/* + * Packs the |iv|, which includes |niv| entries, in the |buf|, + * assuming the |buf| has at least 8 * |niv| bytes. + * + * Returns the number of bytes written into the |buf|. + */ +size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, + const nghttp2_settings_entry *iv, + size_t niv); + +void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, + const uint8_t *payload); + +/* + * Initializes payload of frame->settings. The |frame| takes + * ownership of |iv|. + */ +void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, + nghttp2_settings_entry *iv, + size_t niv); + +/* + * Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are + * assigned to the |*niv_ptr|. This function allocates enough memory + * to store the result in |*iv_ptr|. The caller is responsible to free + * |*iv_ptr| after its use. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, + size_t *niv_ptr, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs PUSH_PROMISE frame |frame| in wire format and store it in + * |bufs|. This function expands |bufs| as necessary to store + * frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * frame->hd.length is assigned after length is determined during + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The deflate operation failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater); + +/* + * Unpacks PUSH_PROMISE frame byte sequence into |frame|. This + * function only unapcks bytes that come before name/value header + * block and after possible Pad Length field. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_PROTO + * TODO END_HEADERS flag is not set + */ +int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, + const uint8_t *payload); + +/* + * Packs PING frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); + +/* + * Unpacks PING wire format into |frame|. + */ +void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, + const uint8_t *payload); + +/* + * Packs GOAWAY frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. + */ +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame); + +/* + * Unpacks GOAWAY wire format into |frame|. The |payload| of length + * |payloadlen| contains first 8 bytes of payload. The + * |var_gift_payload| of length |var_gift_payloadlen| contains + * remaining payload and its buffer is gifted to the function and then + * |frame|. The |var_gift_payloadlen| must be freed by + * nghttp2_frame_goaway_free(). + */ +void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, + const uint8_t *payload, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen); + +/* + * Unpacks GOAWAY wire format into |frame|. This function only exists + * for unit test. After allocating buffer for debug data, this + * function internally calls nghttp2_frame_unpack_goaway_payload(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it + * in |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame); + +/* + * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. + */ +void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + const uint8_t *payload); + +/* + * Packs ALTSVC frame |frame| in wire frame format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext); + +/* + * Unpacks ALTSVC wire format into |frame|. The |payload| of + * |payloadlen| bytes contains frame payload. This function assumes + * that frame->payload points to the nghttp2_ext_altsvc object. + * + * This function always succeeds and returns 0. + */ +void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, + size_t origin_len, uint8_t *payload, + size_t payloadlen); + +/* + * Unpacks ALTSVC wire format into |frame|. This function only exists + * for unit test. After allocating buffer for fields, this function + * internally calls nghttp2_frame_unpack_altsvc_payload(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The payload is too small. + */ +int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs ORIGIN frame |frame| in wire frame format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. + */ +int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *ext); + +/* + * Unpacks ORIGIN wire format into |frame|. The |payload| of length + * |payloadlen| contains the frame payload. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The payload is too small. + */ +int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); +/* + * Initializes HEADERS frame |frame| with given values. |frame| takes + * ownership of |nva|, so caller must not free it. If |stream_id| is + * not assigned yet, it must be -1. + */ +void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, + int32_t stream_id, nghttp2_headers_category cat, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva, size_t nvlen); + +void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem); + +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +void nghttp2_frame_priority_free(nghttp2_priority *frame); + +void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, + uint32_t error_code); + +void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame); + +/* + * Initializes PUSH_PROMISE frame |frame| with given values. |frame| + * takes ownership of |nva|, so caller must not free it. + */ +void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, + int32_t stream_id, + int32_t promised_stream_id, + nghttp2_nv *nva, size_t nvlen); + +void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, + nghttp2_mem *mem); + +/* + * Initializes SETTINGS frame |frame| with given values. |frame| takes + * ownership of |iv|, so caller must not free it. The |flags| are + * bitwise-OR of one or more of nghttp2_settings_flag. + */ +void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, + nghttp2_settings_entry *iv, size_t niv); + +void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem); + +/* + * Initializes PING frame |frame| with given values. If the + * |opqeue_data| is not NULL, it must point to 8 bytes memory region + * of data. The data pointed by |opaque_data| is copied. It can be + * NULL. In this case, 8 bytes NULL is used. + */ +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opque_data); + +void nghttp2_frame_ping_free(nghttp2_ping *frame); + +/* + * Initializes GOAWAY frame |frame| with given values. On success, + * this function takes ownership of |opaque_data|, so caller must not + * free it. If the |opaque_data_len| is 0, opaque_data could be NULL. + */ +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + uint32_t error_code, uint8_t *opaque_data, + size_t opaque_data_len); + +void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem); + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, int32_t stream_id, + int32_t window_size_increment); + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame); + +void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type, + uint8_t flags, int32_t stream_id, + void *payload); + +void nghttp2_frame_extension_free(nghttp2_extension *frame); + +/* + * Initializes ALTSVC frame |frame| with given values. This function + * assumes that frame->payload points to nghttp2_ext_altsvc object. + * Also |origin| and |field_value| are allocated in single buffer, + * starting |origin|. On success, this function takes ownership of + * |origin|, so caller must not free it. + */ +void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id, + uint8_t *origin, size_t origin_len, + uint8_t *field_value, size_t field_value_len); + +/* + * Frees up resources under |frame|. This function does not free + * nghttp2_ext_altsvc object pointed by frame->payload. This function + * only frees origin pointed by nghttp2_ext_altsvc.origin. Therefore, + * other fields must be allocated in the same buffer with origin. + */ +void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem); + +/* + * Initializes ORIGIN frame |frame| with given values. This function + * assumes that frame->payload points to nghttp2_ext_origin object. + * Also |ov| and the memory pointed by the field of its elements are + * allocated in single buffer, starting with |ov|. On success, this + * function takes ownership of |ov|, so caller must not free it. + */ +void nghttp2_frame_origin_init(nghttp2_extension *frame, + nghttp2_origin_entry *ov, size_t nov); + +/* + * Frees up resources under |frame|. This function does not free + * nghttp2_ext_origin object pointed by frame->payload. This function + * only frees nghttp2_ext_origin.ov. Therefore, other fields must be + * allocated in the same buffer with ov. + */ +void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem); + +/* + * Returns the number of padding bytes after payload. The total + * padding length is given in the |padlen|. The returned value does + * not include the Pad Length field. If |padlen| is 0, this function + * returns 0, regardless of frame->hd.flags. + */ +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen); + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id); + +void nghttp2_frame_data_free(nghttp2_data *frame); + +/* + * Makes copy of |iv| and return the copy. The |niv| is the number of + * entries in |iv|. This function returns the pointer to the copy if + * it succeeds, or NULL. + */ +nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem); + +/* + * Sorts the |nva| in ascending order of name and value. If names are + * equivalent, sort them by value. + */ +void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen); + +/* + * Copies name/value pairs from |nva|, which contains |nvlen| pairs, + * to |*nva_ptr|, which is dynamically allocated so that all items can + * be stored. The resultant name and value in nghttp2_nv are + * guaranteed to be NULL-terminated even if the input is not + * null-terminated. + * + * The |*nva_ptr| must be freed using nghttp2_nv_array_del(). + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem); + +/* + * Returns nonzero if the name/value pair |a| equals to |b|. The name + * is compared in case-sensitive, because we ensure that this function + * is called after the name is lower-cased. + */ +int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b); + +/* + * Frees |nva|. + */ +void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem); + +/* + * Checks that the |iv|, which includes |niv| entries, does not have + * invalid values. + * + * This function returns nonzero if it succeeds, or 0. + */ +int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); + +/* + * Sets Pad Length field and flags and adjusts frame header position + * of each buffers in |bufs|. The number of padding is given in the + * |padlen| including Pad Length field. The |hd| is the frame header + * for the serialized data. This function fills zeros padding region + * unless framehd_only is nonzero. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the resulting frame is too large. + */ +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, int framehd_only); + +#endif /* NGHTTP2_FRAME_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_hd.c b/Utilities/cmnghttp2/lib/nghttp2_hd.c new file mode 100644 index 00000000000..5e869315259 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_hd.c @@ -0,0 +1,2351 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd.h" + +#include +#include +#include + +#include "nghttp2_helper.h" +#include "nghttp2_int.h" +#include "nghttp2_debug.h" + +/* Make scalar initialization form of nghttp2_hd_entry */ +#define MAKE_STATIC_ENT(N, V, T, H) \ + { \ + {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ + {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ + {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \ + T, H \ + } + +/* Generated by mkstatictbl.py */ +/* 3rd parameter is nghttp2_token value for header field name. We use + first enum value if same header names are repeated (e.g., + :status). */ +static const nghttp2_hd_static_entry static_table[] = { + MAKE_STATIC_ENT(":authority", "", 0, 3153725150u), + MAKE_STATIC_ENT(":method", "GET", 1, 695666056u), + MAKE_STATIC_ENT(":method", "POST", 1, 695666056u), + MAKE_STATIC_ENT(":path", "/", 3, 3292848686u), + MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u), + MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u), + MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u), + MAKE_STATIC_ENT(":status", "200", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "204", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "206", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "304", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "400", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "404", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "500", 7, 4000288983u), + MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u), + MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u), + MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u), + MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u), + MAKE_STATIC_ENT("accept", "", 18, 136609321u), + MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u), + MAKE_STATIC_ENT("age", "", 20, 742476188u), + MAKE_STATIC_ENT("allow", "", 21, 2930878514u), + MAKE_STATIC_ENT("authorization", "", 22, 2436257726u), + MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u), + MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u), + MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u), + MAKE_STATIC_ENT("content-language", "", 26, 24973587u), + MAKE_STATIC_ENT("content-length", "", 27, 1308181789u), + MAKE_STATIC_ENT("content-location", "", 28, 2302364718u), + MAKE_STATIC_ENT("content-range", "", 29, 3555523146u), + MAKE_STATIC_ENT("content-type", "", 30, 4244048277u), + MAKE_STATIC_ENT("cookie", "", 31, 2007449791u), + MAKE_STATIC_ENT("date", "", 32, 3564297305u), + MAKE_STATIC_ENT("etag", "", 33, 113792960u), + MAKE_STATIC_ENT("expect", "", 34, 2530896728u), + MAKE_STATIC_ENT("expires", "", 35, 1049544579u), + MAKE_STATIC_ENT("from", "", 36, 2513272949u), + MAKE_STATIC_ENT("host", "", 37, 2952701295u), + MAKE_STATIC_ENT("if-match", "", 38, 3597694698u), + MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u), + MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u), + MAKE_STATIC_ENT("if-range", "", 41, 2340978238u), + MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u), + MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u), + MAKE_STATIC_ENT("link", "", 44, 232457833u), + MAKE_STATIC_ENT("location", "", 45, 200649126u), + MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u), + MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u), + MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u), + MAKE_STATIC_ENT("range", "", 49, 4208725202u), + MAKE_STATIC_ENT("referer", "", 50, 3969579366u), + MAKE_STATIC_ENT("refresh", "", 51, 3572655668u), + MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u), + MAKE_STATIC_ENT("server", "", 53, 1085029842u), + MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u), + MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u), + MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u), + MAKE_STATIC_ENT("user-agent", "", 57, 606444526u), + MAKE_STATIC_ENT("vary", "", 58, 1085005381u), + MAKE_STATIC_ENT("via", "", 59, 1762798611u), + MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u), +}; + +static int memeq(const void *s1, const void *s2, size_t n) { + return memcmp(s1, s2, n) == 0; +} + +/* + * This function was generated by genlibtokenlookup.py. Inspired by + * h2o header lookup. https://github.com/h2o/h2o + */ +static int32_t lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (memeq("t", name, 1)) { + return NGHTTP2_TOKEN_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'a': + if (memeq("vi", name, 2)) { + return NGHTTP2_TOKEN_VIA; + } + break; + case 'e': + if (memeq("ag", name, 2)) { + return NGHTTP2_TOKEN_AGE; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'e': + if (memeq("dat", name, 3)) { + return NGHTTP2_TOKEN_DATE; + } + break; + case 'g': + if (memeq("eta", name, 3)) { + return NGHTTP2_TOKEN_ETAG; + } + break; + case 'k': + if (memeq("lin", name, 3)) { + return NGHTTP2_TOKEN_LINK; + } + break; + case 'm': + if (memeq("fro", name, 3)) { + return NGHTTP2_TOKEN_FROM; + } + break; + case 't': + if (memeq("hos", name, 3)) { + return NGHTTP2_TOKEN_HOST; + } + break; + case 'y': + if (memeq("var", name, 3)) { + return NGHTTP2_TOKEN_VARY; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'e': + if (memeq("rang", name, 4)) { + return NGHTTP2_TOKEN_RANGE; + } + break; + case 'h': + if (memeq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + break; + case 'w': + if (memeq("allo", name, 4)) { + return NGHTTP2_TOKEN_ALLOW; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (memeq("cooki", name, 5)) { + return NGHTTP2_TOKEN_COOKIE; + } + break; + case 'r': + if (memeq("serve", name, 5)) { + return NGHTTP2_TOKEN_SERVER; + } + break; + case 't': + if (memeq("accep", name, 5)) { + return NGHTTP2_TOKEN_ACCEPT; + } + if (memeq("expec", name, 5)) { + return NGHTTP2_TOKEN_EXPECT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'd': + if (memeq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + break; + case 'e': + if (memeq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (memeq("upgrad", name, 6)) { + return NGHTTP2_TOKEN_UPGRADE; + } + break; + case 'h': + if (memeq("refres", name, 6)) { + return NGHTTP2_TOKEN_REFRESH; + } + break; + case 'r': + if (memeq("refere", name, 6)) { + return NGHTTP2_TOKEN_REFERER; + } + break; + case 's': + if (memeq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (memeq("expire", name, 6)) { + return NGHTTP2_TOKEN_EXPIRES; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'e': + if (memeq("if-rang", name, 7)) { + return NGHTTP2_TOKEN_IF_RANGE; + } + break; + case 'h': + if (memeq("if-matc", name, 7)) { + return NGHTTP2_TOKEN_IF_MATCH; + } + break; + case 'n': + if (memeq("locatio", name, 7)) { + return NGHTTP2_TOKEN_LOCATION; + } + break; + } + break; + case 9: + switch (name[8]) { + case 'l': + if (memeq(":protoco", name, 8)) { + return NGHTTP2_TOKEN__PROTOCOL; + } + break; + } + break; + case 10: + switch (name[9]) { + case 'e': + if (memeq("keep-aliv", name, 9)) { + return NGHTTP2_TOKEN_KEEP_ALIVE; + } + if (memeq("set-cooki", name, 9)) { + return NGHTTP2_TOKEN_SET_COOKIE; + } + break; + case 'n': + if (memeq("connectio", name, 9)) { + return NGHTTP2_TOKEN_CONNECTION; + } + break; + case 't': + if (memeq("user-agen", name, 9)) { + return NGHTTP2_TOKEN_USER_AGENT; + } + break; + case 'y': + if (memeq(":authorit", name, 9)) { + return NGHTTP2_TOKEN__AUTHORITY; + } + break; + } + break; + case 11: + switch (name[10]) { + case 'r': + if (memeq("retry-afte", name, 10)) { + return NGHTTP2_TOKEN_RETRY_AFTER; + } + break; + } + break; + case 12: + switch (name[11]) { + case 'e': + if (memeq("content-typ", name, 11)) { + return NGHTTP2_TOKEN_CONTENT_TYPE; + } + break; + case 's': + if (memeq("max-forward", name, 11)) { + return NGHTTP2_TOKEN_MAX_FORWARDS; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'd': + if (memeq("last-modifie", name, 12)) { + return NGHTTP2_TOKEN_LAST_MODIFIED; + } + break; + case 'e': + if (memeq("content-rang", name, 12)) { + return NGHTTP2_TOKEN_CONTENT_RANGE; + } + break; + case 'h': + if (memeq("if-none-matc", name, 12)) { + return NGHTTP2_TOKEN_IF_NONE_MATCH; + } + break; + case 'l': + if (memeq("cache-contro", name, 12)) { + return NGHTTP2_TOKEN_CACHE_CONTROL; + } + break; + case 'n': + if (memeq("authorizatio", name, 12)) { + return NGHTTP2_TOKEN_AUTHORIZATION; + } + break; + case 's': + if (memeq("accept-range", name, 12)) { + return NGHTTP2_TOKEN_ACCEPT_RANGES; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (memeq("content-lengt", name, 13)) { + return NGHTTP2_TOKEN_CONTENT_LENGTH; + } + break; + case 't': + if (memeq("accept-charse", name, 13)) { + return NGHTTP2_TOKEN_ACCEPT_CHARSET; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (memeq("accept-languag", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (memeq("accept-encodin", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_ENCODING; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'e': + if (memeq("content-languag", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LANGUAGE; + } + if (memeq("www-authenticat", name, 15)) { + return NGHTTP2_TOKEN_WWW_AUTHENTICATE; + } + break; + case 'g': + if (memeq("content-encodin", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_ENCODING; + } + break; + case 'n': + if (memeq("content-locatio", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LOCATION; + } + if (memeq("proxy-connectio", name, 15)) { + return NGHTTP2_TOKEN_PROXY_CONNECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (memeq("if-modified-sinc", name, 16)) { + return NGHTTP2_TOKEN_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (memeq("transfer-encodin", name, 16)) { + return NGHTTP2_TOKEN_TRANSFER_ENCODING; + } + break; + } + break; + case 18: + switch (name[17]) { + case 'e': + if (memeq("proxy-authenticat", name, 17)) { + return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; + } + break; + } + break; + case 19: + switch (name[18]) { + case 'e': + if (memeq("if-unmodified-sinc", name, 18)) { + return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE; + } + break; + case 'n': + if (memeq("content-dispositio", name, 18)) { + return NGHTTP2_TOKEN_CONTENT_DISPOSITION; + } + if (memeq("proxy-authorizatio", name, 18)) { + return NGHTTP2_TOKEN_PROXY_AUTHORIZATION; + } + break; + } + break; + case 25: + switch (name[24]) { + case 'y': + if (memeq("strict-transport-securit", name, 24)) { + return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; + } + break; + } + break; + case 27: + switch (name[26]) { + case 'n': + if (memeq("access-control-allow-origi", name, 26)) { + return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; + } + break; + } + break; + } + return -1; +} + +void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv) { + ent->nv = *nv; + ent->cnv.name = nv->name->base; + ent->cnv.namelen = nv->name->len; + ent->cnv.value = nv->value->base; + ent->cnv.valuelen = nv->value->len; + ent->cnv.flags = nv->flags; + ent->next = NULL; + ent->hash = 0; + + nghttp2_rcbuf_incref(ent->nv.name); + nghttp2_rcbuf_incref(ent->nv.value); +} + +void nghttp2_hd_entry_free(nghttp2_hd_entry *ent) { + nghttp2_rcbuf_decref(ent->nv.value); + nghttp2_rcbuf_decref(ent->nv.name); +} + +static int name_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) { + return a->name->len == b->namelen && + memeq(a->name->base, b->name, b->namelen); +} + +static int value_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) { + return a->value->len == b->valuelen && + memeq(a->value->base, b->value, b->valuelen); +} + +static uint32_t name_hash(const nghttp2_nv *nv) { + /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */ + uint32_t h = 2166136261u; + size_t i; + + for (i = 0; i < nv->namelen; ++i) { + h ^= nv->name[i]; + h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); + } + + return h; +} + +static void hd_map_init(nghttp2_hd_map *map) { + memset(map, 0, sizeof(nghttp2_hd_map)); +} + +static void hd_map_insert(nghttp2_hd_map *map, nghttp2_hd_entry *ent) { + nghttp2_hd_entry **bucket; + + bucket = &map->table[ent->hash & (HD_MAP_SIZE - 1)]; + + if (*bucket == NULL) { + *bucket = ent; + return; + } + + /* lower index is linked near the root */ + ent->next = *bucket; + *bucket = ent; +} + +static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match, + const nghttp2_nv *nv, int32_t token, + uint32_t hash, int name_only) { + nghttp2_hd_entry *p; + nghttp2_hd_entry *res = NULL; + + *exact_match = 0; + + for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) { + if (token != p->nv.token || + (token == -1 && (hash != p->hash || !name_eq(&p->nv, nv)))) { + continue; + } + if (!res) { + res = p; + if (name_only) { + break; + } + } + if (value_eq(&p->nv, nv)) { + res = p; + *exact_match = 1; + break; + } + } + + return res; +} + +static void hd_map_remove(nghttp2_hd_map *map, nghttp2_hd_entry *ent) { + nghttp2_hd_entry **dst; + + dst = &map->table[ent->hash & (HD_MAP_SIZE - 1)]; + + for (; *dst; dst = &(*dst)->next) { + if (*dst != ent) { + continue; + } + + *dst = ent->next; + ent->next = NULL; + return; + } +} + +static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, + nghttp2_mem *mem) { + size_t size; + for (size = 1; size < bufsize; size <<= 1) + ; + ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); + if (ringbuf->buffer == NULL) { + return NGHTTP2_ERR_NOMEM; + } + ringbuf->mask = size - 1; + ringbuf->first = 0; + ringbuf->len = 0; + return 0; +} + +static nghttp2_hd_entry *hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf, + size_t idx) { + assert(idx < ringbuf->len); + return ringbuf->buffer[(ringbuf->first + idx) & ringbuf->mask]; +} + +static int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, + nghttp2_mem *mem) { + size_t i; + size_t size; + nghttp2_hd_entry **buffer; + + if (ringbuf->mask + 1 >= bufsize) { + return 0; + } + for (size = 1; size < bufsize; size <<= 1) + ; + buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); + if (buffer == NULL) { + return NGHTTP2_ERR_NOMEM; + } + for (i = 0; i < ringbuf->len; ++i) { + buffer[i] = hd_ringbuf_get(ringbuf, i); + } + nghttp2_mem_free(mem, ringbuf->buffer); + ringbuf->buffer = buffer; + ringbuf->mask = size - 1; + ringbuf->first = 0; + return 0; +} + +static void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) { + size_t i; + if (ringbuf == NULL) { + return; + } + for (i = 0; i < ringbuf->len; ++i) { + nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i); + + nghttp2_hd_entry_free(ent); + nghttp2_mem_free(mem, ent); + } + nghttp2_mem_free(mem, ringbuf->buffer); +} + +static int hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf, + nghttp2_hd_entry *ent, nghttp2_mem *mem) { + int rv; + + rv = hd_ringbuf_reserve(ringbuf, ringbuf->len + 1, mem); + + if (rv != 0) { + return rv; + } + + ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent; + ++ringbuf->len; + + return 0; +} + +static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) { + assert(ringbuf->len > 0); + --ringbuf->len; +} + +static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { + int rv; + context->mem = mem; + context->bad = 0; + context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + rv = hd_ringbuf_init( + &context->hd_table, + context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem); + if (rv != 0) { + return rv; + } + + context->hd_table_bufsize = 0; + context->next_seq = 0; + + return 0; +} + +static void hd_context_free(nghttp2_hd_context *context) { + hd_ringbuf_free(&context->hd_table, context->mem); +} + +int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) { + return nghttp2_hd_deflate_init2( + deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); +} + +int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, + size_t max_deflate_dynamic_table_size, + nghttp2_mem *mem) { + int rv; + rv = hd_context_init(&deflater->ctx, mem); + if (rv != 0) { + return rv; + } + + hd_map_init(&deflater->map); + + if (max_deflate_dynamic_table_size < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) { + deflater->notify_table_size_change = 1; + deflater->ctx.hd_table_bufsize_max = max_deflate_dynamic_table_size; + } else { + deflater->notify_table_size_change = 0; + } + + deflater->deflate_hd_table_bufsize_max = max_deflate_dynamic_table_size; + deflater->min_hd_table_bufsize_max = UINT32_MAX; + + return 0; +} + +int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) { + int rv; + + rv = hd_context_init(&inflater->ctx, mem); + if (rv != 0) { + goto fail; + } + + inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + inflater->min_hd_table_bufsize_max = UINT32_MAX; + + inflater->nv_name_keep = NULL; + inflater->nv_value_keep = NULL; + + inflater->opcode = NGHTTP2_HD_OPCODE_NONE; + inflater->state = NGHTTP2_HD_STATE_INFLATE_START; + + nghttp2_buf_init(&inflater->namebuf); + nghttp2_buf_init(&inflater->valuebuf); + + inflater->namercbuf = NULL; + inflater->valuercbuf = NULL; + + inflater->huffman_encoded = 0; + inflater->index = 0; + inflater->left = 0; + inflater->shift = 0; + inflater->index_required = 0; + inflater->no_index = 0; + + return 0; + +fail: + return rv; +} + +static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) { + nghttp2_rcbuf_decref(inflater->nv_value_keep); + nghttp2_rcbuf_decref(inflater->nv_name_keep); + + inflater->nv_value_keep = NULL; + inflater->nv_name_keep = NULL; +} + +void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) { + hd_context_free(&deflater->ctx); +} + +void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) { + hd_inflate_keep_free(inflater); + + nghttp2_rcbuf_decref(inflater->valuercbuf); + nghttp2_rcbuf_decref(inflater->namercbuf); + + hd_context_free(&inflater->ctx); +} + +static size_t entry_room(size_t namelen, size_t valuelen) { + return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; +} + +static void emit_header(nghttp2_hd_nv *nv_out, nghttp2_hd_nv *nv) { + DEBUGF("inflatehd: header emission: %s: %s\n", nv->name->base, + nv->value->base); + /* ent->ref may be 0. This happens if the encoder emits literal + block larger than header table capacity with indexing. */ + *nv_out = *nv; +} + +static size_t count_encoded_length(size_t n, size_t prefix) { + size_t k = (size_t)((1 << prefix) - 1); + size_t len = 0; + + if (n < k) { + return 1; + } + + n -= k; + ++len; + + for (; n >= 128; n >>= 7, ++len) + ; + + return len + 1; +} + +static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { + size_t k = (size_t)((1 << prefix) - 1); + uint8_t *begin = buf; + + *buf = (uint8_t)(*buf & ~k); + + if (n < k) { + *buf = (uint8_t)(*buf | n); + return 1; + } + + *buf = (uint8_t)(*buf | k); + ++buf; + + n -= k; + + for (; n >= 128; n >>= 7) { + *buf++ = (uint8_t)((1 << 7) | (n & 0x7f)); + } + + *buf++ = (uint8_t)n; + + return (size_t)(buf - begin); +} + +/* + * Decodes |prefix| prefixed integer stored from |in|. The |last| + * represents the 1 beyond the last of the valid contiguous memory + * region from |in|. The decoded integer must be less than or equal + * to UINT32_MAX. + * + * If the |initial| is nonzero, it is used as a initial value, this + * function assumes the |in| starts with intermediate data. + * + * An entire integer is decoded successfully, decoded, the |*fin| is + * set to nonzero. + * + * This function stores the decoded integer in |*res| if it succeed, + * including partial decoding (in this case, number of shift to make + * in the next call will be stored in |*shift_ptr|) and returns number + * of bytes processed, or returns -1, indicating decoding error. + */ +static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, + uint32_t initial, size_t shift, const uint8_t *in, + const uint8_t *last, size_t prefix) { + uint32_t k = (uint8_t)((1 << prefix) - 1); + uint32_t n = initial; + const uint8_t *start = in; + + *shift_ptr = 0; + *fin = 0; + + if (n == 0) { + if ((*in & k) != k) { + *res = (*in) & k; + *fin = 1; + return 1; + } + + n = k; + + if (++in == last) { + *res = n; + return (ssize_t)(in - start); + } + } + + for (; in != last; ++in, shift += 7) { + uint32_t add = *in & 0x7f; + + if (shift >= 32) { + DEBUGF("inflate: shift exponent overflow\n"); + return -1; + } + + if ((UINT32_MAX >> shift) < add) { + DEBUGF("inflate: integer overflow on shift\n"); + return -1; + } + + add <<= shift; + + if (UINT32_MAX - add < n) { + DEBUGF("inflate: integer overflow on addition\n"); + return -1; + } + + n += add; + + if ((*in & (1 << 7)) == 0) { + break; + } + } + + *shift_ptr = shift; + + if (in == last) { + *res = n; + return (ssize_t)(in - start); + } + + *res = n; + *fin = 1; + return (ssize_t)(in + 1 - start); +} + +static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { + int rv; + uint8_t *bufp; + size_t blocklen; + uint8_t sb[16]; + + DEBUGF("deflatehd: emit table_size=%zu\n", table_size); + + blocklen = count_encoded_length(table_size, 5); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + + *bufp = 0x20u; + + encode_length(bufp, table_size, 5); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) { + int rv; + size_t blocklen; + uint8_t sb[16]; + uint8_t *bufp; + + blocklen = count_encoded_length(idx + 1, 7); + + DEBUGF("deflatehd: emit indexed index=%zu, %zu bytes\n", idx, blocklen); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + *bufp = 0x80u; + encode_length(bufp, idx + 1, 7); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { + int rv; + uint8_t sb[16]; + uint8_t *bufp; + size_t blocklen; + size_t enclen; + int huffman = 0; + + enclen = nghttp2_hd_huff_encode_count(str, len); + + if (enclen < len) { + huffman = 1; + } else { + enclen = len; + } + + blocklen = count_encoded_length(enclen, 7); + + DEBUGF("deflatehd: emit string str=%.*s, length=%zu, huffman=%d, " + "encoded_length=%zu\n", + (int)len, (const char *)str, len, huffman, enclen); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + *bufp = huffman ? 1 << 7 : 0; + encode_length(bufp, enclen, 7); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + if (huffman) { + rv = nghttp2_hd_huff_encode(bufs, str, len); + } else { + assert(enclen == len); + rv = nghttp2_bufs_add(bufs, str, len); + } + + return rv; +} + +static uint8_t pack_first_byte(int indexing_mode) { + switch (indexing_mode) { + case NGHTTP2_HD_WITH_INDEXING: + return 0x40u; + case NGHTTP2_HD_WITHOUT_INDEXING: + return 0; + case NGHTTP2_HD_NEVER_INDEXING: + return 0x10u; + default: + assert(0); + } + /* This is required to compile with android NDK r10d + + --enable-werror */ + return 0; +} + +static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, + const nghttp2_nv *nv, int indexing_mode) { + int rv; + uint8_t *bufp; + size_t blocklen; + uint8_t sb[16]; + size_t prefixlen; + + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { + prefixlen = 6; + } else { + prefixlen = 4; + } + + DEBUGF("deflatehd: emit indname index=%zu, valuelen=%zu, indexing_mode=%d\n", + idx, nv->valuelen, indexing_mode); + + blocklen = count_encoded_length(idx + 1, prefixlen); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + + *bufp = pack_first_byte(indexing_mode); + + encode_length(bufp, idx + 1, prefixlen); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->value, nv->valuelen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, + int indexing_mode) { + int rv; + + DEBUGF( + "deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\n", + nv->namelen, nv->valuelen, indexing_mode); + + rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->name, nv->namelen); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->value, nv->valuelen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int add_hd_table_incremental(nghttp2_hd_context *context, + nghttp2_hd_nv *nv, nghttp2_hd_map *map, + uint32_t hash) { + int rv; + nghttp2_hd_entry *new_ent; + size_t room; + nghttp2_mem *mem; + + mem = context->mem; + room = entry_room(nv->name->len, nv->value->len); + + while (context->hd_table_bufsize + room > context->hd_table_bufsize_max && + context->hd_table.len > 0) { + + size_t idx = context->hd_table.len - 1; + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); + + context->hd_table_bufsize -= + entry_room(ent->nv.name->len, ent->nv.value->len); + + DEBUGF("hpack: remove item from header table: %s: %s\n", + (char *)ent->nv.name->base, (char *)ent->nv.value->base); + + hd_ringbuf_pop_back(&context->hd_table); + if (map) { + hd_map_remove(map, ent); + } + + nghttp2_hd_entry_free(ent); + nghttp2_mem_free(mem, ent); + } + + if (room > context->hd_table_bufsize_max) { + /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is + immediately evicted. So we don't allocate memory for it. */ + return 0; + } + + new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry)); + if (new_ent == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_hd_entry_init(new_ent, nv); + + rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem); + + if (rv != 0) { + nghttp2_hd_entry_free(new_ent); + nghttp2_mem_free(mem, new_ent); + + return rv; + } + + new_ent->seq = context->next_seq++; + new_ent->hash = hash; + + if (map) { + hd_map_insert(map, new_ent); + } + + context->hd_table_bufsize += room; + + return 0; +} + +typedef struct { + ssize_t index; + /* Nonzero if both name and value are matched. */ + int name_value_match; +} search_result; + +static search_result search_static_table(const nghttp2_nv *nv, int32_t token, + int name_only) { + search_result res = {token, 0}; + int i; + const nghttp2_hd_static_entry *ent; + + if (name_only) { + return res; + } + + for (i = token; + i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token; + ++i) { + ent = &static_table[i]; + if (ent->value.len == nv->valuelen && + memcmp(ent->value.base, nv->value, nv->valuelen) == 0) { + res.index = i; + res.name_value_match = 1; + return res; + } + } + return res; +} + +static search_result search_hd_table(nghttp2_hd_context *context, + const nghttp2_nv *nv, int32_t token, + int indexing_mode, nghttp2_hd_map *map, + uint32_t hash) { + search_result res = {-1, 0}; + const nghttp2_hd_entry *ent; + int exact_match; + int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING; + + exact_match = 0; + ent = hd_map_find(map, &exact_match, nv, token, hash, name_only); + + if (!exact_match && token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { + return search_static_table(nv, token, name_only); + } + + if (ent == NULL) { + return res; + } + + res.index = + (ssize_t)(context->next_seq - 1 - ent->seq + NGHTTP2_STATIC_TABLE_LENGTH); + res.name_value_match = exact_match; + + return res; +} + +static void hd_context_shrink_table_size(nghttp2_hd_context *context, + nghttp2_hd_map *map) { + nghttp2_mem *mem; + + mem = context->mem; + + while (context->hd_table_bufsize > context->hd_table_bufsize_max && + context->hd_table.len > 0) { + size_t idx = context->hd_table.len - 1; + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); + context->hd_table_bufsize -= + entry_room(ent->nv.name->len, ent->nv.value->len); + hd_ringbuf_pop_back(&context->hd_table); + if (map) { + hd_map_remove(map, ent); + } + + nghttp2_hd_entry_free(ent); + nghttp2_mem_free(mem, ent); + } +} + +int nghttp2_hd_deflate_change_table_size( + nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) { + size_t next_bufsize = nghttp2_min(settings_max_dynamic_table_size, + deflater->deflate_hd_table_bufsize_max); + + deflater->ctx.hd_table_bufsize_max = next_bufsize; + + deflater->min_hd_table_bufsize_max = + nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize); + + deflater->notify_table_size_change = 1; + + hd_context_shrink_table_size(&deflater->ctx, &deflater->map); + return 0; +} + +int nghttp2_hd_inflate_change_table_size( + nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) { + switch (inflater->state) { + case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: + case NGHTTP2_HD_STATE_INFLATE_START: + break; + default: + return NGHTTP2_ERR_INVALID_STATE; + } + + /* It seems that encoder is not required to send dynamic table size + update if the table size is not changed after applying + SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this + is the intention of the editor. If new maximum table size is + strictly smaller than the current negotiated maximum size, + encoder must send dynamic table size update. In other cases, we + cannot expect it to do so. */ + if (inflater->ctx.hd_table_bufsize_max > settings_max_dynamic_table_size) { + inflater->state = NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE; + /* Remember minimum value, and validate that encoder sends the + value less than or equal to this. */ + inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size; + } + + inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; + + inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; + + hd_context_shrink_table_size(&inflater->ctx, NULL); + return 0; +} + +#define INDEX_RANGE_VALID(context, idx) \ + ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH) + +static size_t get_max_index(nghttp2_hd_context *context) { + return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH; +} + +nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) { + assert(INDEX_RANGE_VALID(context, idx)); + if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { + return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) + ->nv; + } else { + const nghttp2_hd_static_entry *ent = &static_table[idx]; + nghttp2_hd_nv nv = {(nghttp2_rcbuf *)&ent->name, + (nghttp2_rcbuf *)&ent->value, ent->token, + NGHTTP2_NV_FLAG_NONE}; + return nv; + } +} + +static const nghttp2_nv *nghttp2_hd_table_get2(nghttp2_hd_context *context, + size_t idx) { + assert(INDEX_RANGE_VALID(context, idx)); + if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { + return &hd_ringbuf_get(&context->hd_table, + idx - NGHTTP2_STATIC_TABLE_LENGTH) + ->cnv; + } + + return &static_table[idx].cnv; +} + +static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nv, int32_t token) { + if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE || + token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG || + token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE || + token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || + token == NGHTTP2_TOKEN_SET_COOKIE || + entry_room(nv->namelen, nv->valuelen) > + deflater->ctx.hd_table_bufsize_max * 3 / 4) { + return NGHTTP2_HD_WITHOUT_INDEXING; + } + + return NGHTTP2_HD_WITH_INDEXING; +} + +static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, + const nghttp2_nv *nv) { + int rv; + search_result res; + ssize_t idx; + int indexing_mode; + int32_t token; + nghttp2_mem *mem; + uint32_t hash = 0; + + DEBUGF("deflatehd: deflating %.*s: %.*s\n", (int)nv->namelen, nv->name, + (int)nv->valuelen, nv->value); + + mem = deflater->ctx.mem; + + token = lookup_token(nv->name, nv->namelen); + if (token == -1) { + hash = name_hash(nv); + } else if (token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { + hash = static_table[token].hash; + } + + /* Don't index authorization header field since it may contain low + entropy secret data (e.g., id/password). Also cookie header + field with less than 20 bytes value is also never indexed. This + is the same criteria used in Firefox codebase. */ + indexing_mode = + token == NGHTTP2_TOKEN_AUTHORIZATION || + (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || + (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) + ? NGHTTP2_HD_NEVER_INDEXING + : hd_deflate_decide_indexing(deflater, nv, token); + + res = search_hd_table(&deflater->ctx, nv, token, indexing_mode, + &deflater->map, hash); + + idx = res.index; + + if (res.name_value_match) { + + DEBUGF("deflatehd: name/value match index=%zd\n", idx); + + rv = emit_indexed_block(bufs, (size_t)idx); + if (rv != 0) { + return rv; + } + + return 0; + } + + if (res.index != -1) { + DEBUGF("deflatehd: name match index=%zd\n", res.index); + } + + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { + nghttp2_hd_nv hd_nv; + + if (idx != -1) { + hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name; + nghttp2_rcbuf_incref(hd_nv.name); + } else { + rv = nghttp2_rcbuf_new2(&hd_nv.name, nv->name, nv->namelen, mem); + if (rv != 0) { + return rv; + } + } + + rv = nghttp2_rcbuf_new2(&hd_nv.value, nv->value, nv->valuelen, mem); + + if (rv != 0) { + nghttp2_rcbuf_decref(hd_nv.name); + return rv; + } + + hd_nv.token = token; + hd_nv.flags = NGHTTP2_NV_FLAG_NONE; + + rv = add_hd_table_incremental(&deflater->ctx, &hd_nv, &deflater->map, hash); + + nghttp2_rcbuf_decref(hd_nv.value); + nghttp2_rcbuf_decref(hd_nv.name); + + if (rv != 0) { + return NGHTTP2_ERR_HEADER_COMP; + } + } + if (idx == -1) { + rv = emit_newname_block(bufs, nv, indexing_mode); + } else { + rv = emit_indname_block(bufs, (size_t)idx, nv, indexing_mode); + } + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, const nghttp2_nv *nv, + size_t nvlen) { + size_t i; + int rv = 0; + + if (deflater->ctx.bad) { + return NGHTTP2_ERR_HEADER_COMP; + } + + if (deflater->notify_table_size_change) { + size_t min_hd_table_bufsize_max; + + min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max; + + deflater->notify_table_size_change = 0; + deflater->min_hd_table_bufsize_max = UINT32_MAX; + + if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { + + rv = emit_table_size(bufs, min_hd_table_bufsize_max); + + if (rv != 0) { + goto fail; + } + } + + rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max); + + if (rv != 0) { + goto fail; + } + } + + for (i = 0; i < nvlen; ++i) { + rv = deflate_nv(deflater, bufs, &nv[i]); + if (rv != 0) { + goto fail; + } + } + + DEBUGF("deflatehd: all input name/value pairs were deflated\n"); + + return 0; +fail: + DEBUGF("deflatehd: error return %d\n", rv); + + deflater->ctx.bad = 1; + return rv; +} + +ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, + size_t buflen, const nghttp2_nv *nv, + size_t nvlen) { + nghttp2_bufs bufs; + int rv; + nghttp2_mem *mem; + + mem = deflater->ctx.mem; + + rv = nghttp2_bufs_wrap_init(&bufs, buf, buflen, mem); + + if (rv != 0) { + return rv; + } + + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); + + buflen = nghttp2_bufs_len(&bufs); + + nghttp2_bufs_wrap_free(&bufs); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + if (rv != 0) { + return rv; + } + + return (ssize_t)buflen; +} + +ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, + const nghttp2_vec *vec, size_t veclen, + const nghttp2_nv *nv, size_t nvlen) { + nghttp2_bufs bufs; + int rv; + nghttp2_mem *mem; + size_t buflen; + + mem = deflater->ctx.mem; + + rv = nghttp2_bufs_wrap_init2(&bufs, vec, veclen, mem); + + if (rv != 0) { + return rv; + } + + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); + + buflen = nghttp2_bufs_len(&bufs); + + nghttp2_bufs_wrap_free(&bufs); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + if (rv != 0) { + return rv; + } + + return (ssize_t)buflen; +} + +size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, size_t nvlen) { + size_t n = 0; + size_t i; + (void)deflater; + + /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - + 1 using 4 bit prefix requires 6 bytes. We may emit this at most + twice. */ + n += 12; + + /* Use Literal Header Field without indexing - New Name, since it is + most space consuming format. Also we choose the less one between + non-huffman and huffman, so using literal byte count is + sufficient for upper bound. + + Encoding (1u << 31) - 1 using 7 bit prefix requires 6 bytes. We + need 2 of this for |nvlen| header fields. */ + n += 6 * 2 * nvlen; + + for (i = 0; i < nvlen; ++i) { + n += nva[i].namelen + nva[i].valuelen; + } + + return n; +} + +int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max) { + return nghttp2_hd_deflate_new2(deflater_ptr, deflate_hd_table_bufsize_max, + NULL); +} + +int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem) { + int rv; + nghttp2_hd_deflater *deflater; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + deflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_deflater)); + + if (deflater == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_hd_deflate_init2(deflater, deflate_hd_table_bufsize_max, mem); + + if (rv != 0) { + nghttp2_mem_free(mem, deflater); + + return rv; + } + + *deflater_ptr = deflater; + + return 0; +} + +void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) { + nghttp2_mem *mem; + + mem = deflater->ctx.mem; + + nghttp2_hd_deflate_free(deflater); + + nghttp2_mem_free(mem, deflater); +} + +static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater, + const uint8_t *in) { + inflater->huffman_encoded = (*in & (1 << 7)) != 0; +} + +/* + * Decodes the integer from the range [in, last). The result is + * assigned to |inflater->left|. If the |inflater->left| is 0, then + * it performs variable integer decoding from scratch. Otherwise, it + * uses the |inflater->left| as the initial value and continues to + * decode assuming that [in, last) begins with intermediary sequence. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * Integer decoding failed + */ +static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, + const uint8_t *in, const uint8_t *last, + size_t prefix, size_t maxlen) { + ssize_t rv; + uint32_t out; + + *rfin = 0; + + rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left, + inflater->shift, in, last, prefix); + + if (rv == -1) { + DEBUGF("inflatehd: integer decoding failed\n"); + return NGHTTP2_ERR_HEADER_COMP; + } + + if (out > maxlen) { + DEBUGF("inflatehd: integer exceeded the maximum value %zu\n", maxlen); + return NGHTTP2_ERR_HEADER_COMP; + } + + inflater->left = out; + + DEBUGF("inflatehd: decoded integer is %u\n", out); + + return rv; +} + +/* + * Reads |inflater->left| bytes from the range [in, last) and performs + * huffman decoding against them and pushes the result into the + * |buffer|. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_HEADER_COMP + * Huffman decoding failed + */ +static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, + nghttp2_buf *buf, const uint8_t *in, + const uint8_t *last) { + ssize_t readlen; + int fin = 0; + if ((size_t)(last - in) >= inflater->left) { + last = in + inflater->left; + fin = 1; + } + readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, buf, in, + (size_t)(last - in), fin); + + if (readlen < 0) { + DEBUGF("inflatehd: huffman decoding failed\n"); + return readlen; + } + if (nghttp2_hd_huff_decode_failure_state(&inflater->huff_decode_ctx)) { + DEBUGF("inflatehd: huffman decoding failed\n"); + return NGHTTP2_ERR_HEADER_COMP; + } + + inflater->left -= (size_t)readlen; + return readlen; +} + +/* + * Reads |inflater->left| bytes from the range [in, last) and copies + * them into the |buffer|. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_HEADER_COMP + * Header decompression failed + */ +static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, nghttp2_buf *buf, + const uint8_t *in, const uint8_t *last) { + size_t len = nghttp2_min((size_t)(last - in), inflater->left); + + buf->last = nghttp2_cpymem(buf->last, in, len); + + inflater->left -= len; + return (ssize_t)len; +} + +/* + * Finalize indexed header representation reception. The referenced + * header is always emitted, and |*nv_out| is filled with that value. + */ +static void hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out) { + nghttp2_hd_nv nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index); + + emit_header(nv_out, &nv); +} + +/* + * Finalize literal header representation - new name- reception. If + * header is emitted, |*nv_out| is filled with that value and 0 is + * returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out) { + nghttp2_hd_nv nv; + int rv; + + if (inflater->no_index) { + nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; + } else { + nv.flags = NGHTTP2_NV_FLAG_NONE; + } + + nv.name = inflater->namercbuf; + nv.value = inflater->valuercbuf; + nv.token = lookup_token(inflater->namercbuf->base, inflater->namercbuf->len); + + if (inflater->index_required) { + rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0); + + if (rv != 0) { + return rv; + } + } + + emit_header(nv_out, &nv); + + inflater->nv_name_keep = nv.name; + inflater->nv_value_keep = nv.value; + + inflater->namercbuf = NULL; + inflater->valuercbuf = NULL; + + return 0; +} + +/* + * Finalize literal header representation - indexed name- + * reception. If header is emitted, |*nv_out| is filled with that + * value and 0 is returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out) { + nghttp2_hd_nv nv; + int rv; + + nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index); + + if (inflater->no_index) { + nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; + } else { + nv.flags = NGHTTP2_NV_FLAG_NONE; + } + + nghttp2_rcbuf_incref(nv.name); + + nv.value = inflater->valuercbuf; + + if (inflater->index_required) { + rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0); + if (rv != 0) { + nghttp2_rcbuf_decref(nv.name); + return NGHTTP2_ERR_NOMEM; + } + } + + emit_header(nv_out, &nv); + + inflater->nv_name_keep = nv.name; + inflater->nv_value_keep = nv.value; + + inflater->valuercbuf = NULL; + + return 0; +} + +ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, + int *inflate_flags, uint8_t *in, size_t inlen, + int in_final) { + return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, in, inlen, + in_final); +} + +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, int in_final) { + ssize_t rv; + nghttp2_hd_nv hd_nv; + + rv = nghttp2_hd_inflate_hd_nv(inflater, &hd_nv, inflate_flags, in, inlen, + in_final); + + if (rv < 0) { + return rv; + } + + if (*inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + nv_out->name = hd_nv.name->base; + nv_out->namelen = hd_nv.name->len; + + nv_out->value = hd_nv.value->base; + nv_out->valuelen = hd_nv.value->len; + + nv_out->flags = hd_nv.flags; + } + + return rv; +} + +ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, + int in_final) { + ssize_t rv = 0; + const uint8_t *first = in; + const uint8_t *last = in + inlen; + int rfin = 0; + int busy = 0; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + if (inflater->ctx.bad) { + return NGHTTP2_ERR_HEADER_COMP; + } + + DEBUGF("inflatehd: start state=%d\n", inflater->state); + hd_inflate_keep_free(inflater); + *inflate_flags = NGHTTP2_HD_INFLATE_NONE; + for (; in != last || busy;) { + busy = 0; + switch (inflater->state) { + case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: + if ((*in & 0xe0u) != 0x20u) { + DEBUGF("inflatehd: header table size change was expected, but saw " + "0x%02x as first byte", + *in); + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; + } + /* fall through */ + case NGHTTP2_HD_STATE_INFLATE_START: + case NGHTTP2_HD_STATE_OPCODE: + if ((*in & 0xe0u) == 0x20u) { + DEBUGF("inflatehd: header table size change\n"); + if (inflater->state == NGHTTP2_HD_STATE_OPCODE) { + DEBUGF("inflatehd: header table size change must appear at the head " + "of header block\n"); + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; + } + inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; + inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE; + } else if (*in & 0x80u) { + DEBUGF("inflatehd: indexed repr\n"); + inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; + inflater->state = NGHTTP2_HD_STATE_READ_INDEX; + } else { + if (*in == 0x40u || *in == 0 || *in == 0x10u) { + DEBUGF("inflatehd: literal header repr - new name\n"); + inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME; + inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN; + } else { + DEBUGF("inflatehd: literal header repr - indexed name\n"); + inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME; + inflater->state = NGHTTP2_HD_STATE_READ_INDEX; + } + inflater->index_required = (*in & 0x40) != 0; + inflater->no_index = (*in & 0xf0u) == 0x10u; + DEBUGF("inflatehd: indexing required=%d, no_index=%d\n", + inflater->index_required, inflater->no_index); + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + ++in; + } + } + inflater->left = 0; + inflater->shift = 0; + break; + case NGHTTP2_HD_STATE_READ_TABLE_SIZE: + rfin = 0; + rv = hd_inflate_read_len( + inflater, &rfin, in, last, 5, + nghttp2_min(inflater->min_hd_table_bufsize_max, + inflater->settings_hd_table_bufsize_max)); + if (rv < 0) { + goto fail; + } + in += rv; + if (!rfin) { + goto almost_ok; + } + DEBUGF("inflatehd: table_size=%zu\n", inflater->left); + inflater->min_hd_table_bufsize_max = UINT32_MAX; + inflater->ctx.hd_table_bufsize_max = inflater->left; + hd_context_shrink_table_size(&inflater->ctx, NULL); + inflater->state = NGHTTP2_HD_STATE_INFLATE_START; + break; + case NGHTTP2_HD_STATE_READ_INDEX: { + size_t prefixlen; + + if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { + prefixlen = 7; + } else if (inflater->index_required) { + prefixlen = 6; + } else { + prefixlen = 4; + } + + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, prefixlen, + get_max_index(&inflater->ctx)); + if (rv < 0) { + goto fail; + } + + in += rv; + + if (!rfin) { + goto almost_ok; + } + + if (inflater->left == 0) { + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; + } + + DEBUGF("inflatehd: index=%zu\n", inflater->left); + if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { + inflater->index = inflater->left; + --inflater->index; + + hd_inflate_commit_indexed(inflater, nv_out); + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + return (ssize_t)(in - first); + } else { + inflater->index = inflater->left; + --inflater->index; + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + } + break; + } + case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN: + hd_inflate_set_huffman_encoded(inflater, in); + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN; + inflater->left = 0; + inflater->shift = 0; + DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0); + /* Fall through */ + case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); + if (rv < 0) { + goto fail; + } + in += rv; + if (!rfin) { + DEBUGF("inflatehd: integer not fully decoded. current=%zu\n", + inflater->left); + + goto almost_ok; + } + + if (inflater->huffman_encoded) { + nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); + + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF; + + rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1, + mem); + } else { + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME; + rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left + 1, mem); + } + + if (rv != 0) { + goto fail; + } + + nghttp2_buf_wrap_init(&inflater->namebuf, inflater->namercbuf->base, + inflater->namercbuf->len); + + break; + case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF: + rv = hd_inflate_read_huff(inflater, &inflater->namebuf, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF("inflatehd: %zd bytes read\n", rv); + + if (inflater->left) { + DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); + + goto almost_ok; + } + + *inflater->namebuf.last = '\0'; + inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf); + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + + break; + case NGHTTP2_HD_STATE_NEWNAME_READ_NAME: + rv = hd_inflate_read(inflater, &inflater->namebuf, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF("inflatehd: %zd bytes read\n", rv); + if (inflater->left) { + DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); + + goto almost_ok; + } + + *inflater->namebuf.last = '\0'; + inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf); + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + + break; + case NGHTTP2_HD_STATE_CHECK_VALUELEN: + hd_inflate_set_huffman_encoded(inflater, in); + inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN; + inflater->left = 0; + inflater->shift = 0; + DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0); + /* Fall through */ + case NGHTTP2_HD_STATE_READ_VALUELEN: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); + if (rv < 0) { + goto fail; + } + + in += rv; + + if (!rfin) { + goto almost_ok; + } + + DEBUGF("inflatehd: valuelen=%zu\n", inflater->left); + + if (inflater->huffman_encoded) { + nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); + + inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF; + + rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1, + mem); + } else { + inflater->state = NGHTTP2_HD_STATE_READ_VALUE; + + rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left + 1, mem); + } + + if (rv != 0) { + goto fail; + } + + nghttp2_buf_wrap_init(&inflater->valuebuf, inflater->valuercbuf->base, + inflater->valuercbuf->len); + + busy = 1; + + break; + case NGHTTP2_HD_STATE_READ_VALUEHUFF: + rv = hd_inflate_read_huff(inflater, &inflater->valuebuf, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF("inflatehd: %zd bytes read\n", rv); + + if (inflater->left) { + DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); + + goto almost_ok; + } + + *inflater->valuebuf.last = '\0'; + inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf); + + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + rv = hd_inflate_commit_newname(inflater, nv_out); + } else { + rv = hd_inflate_commit_indname(inflater, nv_out); + } + + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + + return (ssize_t)(in - first); + case NGHTTP2_HD_STATE_READ_VALUE: + rv = hd_inflate_read(inflater, &inflater->valuebuf, in, last); + if (rv < 0) { + DEBUGF("inflatehd: value read failure %zd: %s\n", rv, + nghttp2_strerror((int)rv)); + goto fail; + } + + in += rv; + + DEBUGF("inflatehd: %zd bytes read\n", rv); + + if (inflater->left) { + DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); + goto almost_ok; + } + + *inflater->valuebuf.last = '\0'; + inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf); + + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + rv = hd_inflate_commit_newname(inflater, nv_out); + } else { + rv = hd_inflate_commit_indname(inflater, nv_out); + } + + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + + return (ssize_t)(in - first); + } + } + + assert(in == last); + + DEBUGF("inflatehd: all input bytes were processed\n"); + + if (in_final) { + DEBUGF("inflatehd: in_final set\n"); + + if (inflater->state != NGHTTP2_HD_STATE_OPCODE && + inflater->state != NGHTTP2_HD_STATE_INFLATE_START) { + DEBUGF("inflatehd: unacceptable state=%d\n", inflater->state); + rv = NGHTTP2_ERR_HEADER_COMP; + + goto fail; + } + *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; + } + return (ssize_t)(in - first); + +almost_ok: + if (in_final) { + DEBUGF("inflatehd: input ended prematurely\n"); + + rv = NGHTTP2_ERR_HEADER_COMP; + + goto fail; + } + return (ssize_t)(in - first); + +fail: + DEBUGF("inflatehd: error return %zd\n", rv); + + inflater->ctx.bad = 1; + return rv; +} + +int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) { + hd_inflate_keep_free(inflater); + inflater->state = NGHTTP2_HD_STATE_INFLATE_START; + return 0; +} + +int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) { + return nghttp2_hd_inflate_new2(inflater_ptr, NULL); +} + +int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, + nghttp2_mem *mem) { + int rv; + nghttp2_hd_inflater *inflater; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + inflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_inflater)); + + if (inflater == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_hd_inflate_init(inflater, mem); + + if (rv != 0) { + nghttp2_mem_free(mem, inflater); + + return rv; + } + + *inflater_ptr = inflater; + + return 0; +} + +void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + nghttp2_hd_inflate_free(inflater); + + nghttp2_mem_free(mem, inflater); +} + +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, + nghttp2_nv *nv, int indexing_mode) { + + return emit_indname_block(bufs, idx, nv, indexing_mode); +} + +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, + int indexing_mode) { + return emit_newname_block(bufs, nv, indexing_mode); +} + +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { + return emit_table_size(bufs, table_size); +} + +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix) { + return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix); +} + +static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context, + size_t idx) { + if (idx == 0) { + return NULL; + } + + --idx; + + if (!INDEX_RANGE_VALID(context, idx)) { + return NULL; + } + + return nghttp2_hd_table_get2(context, idx); +} + +size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) { + return get_max_index(&deflater->ctx); +} + +const nghttp2_nv * +nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx) { + return hd_get_table_entry(&deflater->ctx, idx); +} + +size_t +nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater) { + return deflater->ctx.hd_table_bufsize; +} + +size_t +nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) { + return deflater->ctx.hd_table_bufsize_max; +} + +size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) { + return get_max_index(&inflater->ctx); +} + +const nghttp2_nv * +nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx) { + return hd_get_table_entry(&inflater->ctx, idx); +} + +size_t +nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater) { + return inflater->ctx.hd_table_bufsize; +} + +size_t +nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater) { + return inflater->ctx.hd_table_bufsize_max; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_hd.h b/Utilities/cmnghttp2/lib/nghttp2_hd.h new file mode 100644 index 00000000000..267402881f4 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_hd.h @@ -0,0 +1,439 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HD_H +#define NGHTTP2_HD_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp2_hd_huffman.h" +#include "nghttp2_buf.h" +#include "nghttp2_mem.h" +#include "nghttp2_rcbuf.h" + +#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE +#define NGHTTP2_HD_ENTRY_OVERHEAD 32 + +/* The maximum length of one name/value pair. This is the sum of the + length of name and value. This is not specified by the spec. We + just chose the arbitrary size */ +#define NGHTTP2_HD_MAX_NV 65536 + +/* Default size of maximum table buffer size for encoder. Even if + remote decoder notifies larger buffer size for its decoding, + encoder only uses the memory up to this value. */ +#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) + +/* Exported for unit test */ +#define NGHTTP2_STATIC_TABLE_LENGTH 61 + +/* Generated by genlibtokenlookup.py */ +typedef enum { + NGHTTP2_TOKEN__AUTHORITY = 0, + NGHTTP2_TOKEN__METHOD = 1, + NGHTTP2_TOKEN__PATH = 3, + NGHTTP2_TOKEN__SCHEME = 5, + NGHTTP2_TOKEN__STATUS = 7, + NGHTTP2_TOKEN_ACCEPT_CHARSET = 14, + NGHTTP2_TOKEN_ACCEPT_ENCODING = 15, + NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16, + NGHTTP2_TOKEN_ACCEPT_RANGES = 17, + NGHTTP2_TOKEN_ACCEPT = 18, + NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19, + NGHTTP2_TOKEN_AGE = 20, + NGHTTP2_TOKEN_ALLOW = 21, + NGHTTP2_TOKEN_AUTHORIZATION = 22, + NGHTTP2_TOKEN_CACHE_CONTROL = 23, + NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24, + NGHTTP2_TOKEN_CONTENT_ENCODING = 25, + NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26, + NGHTTP2_TOKEN_CONTENT_LENGTH = 27, + NGHTTP2_TOKEN_CONTENT_LOCATION = 28, + NGHTTP2_TOKEN_CONTENT_RANGE = 29, + NGHTTP2_TOKEN_CONTENT_TYPE = 30, + NGHTTP2_TOKEN_COOKIE = 31, + NGHTTP2_TOKEN_DATE = 32, + NGHTTP2_TOKEN_ETAG = 33, + NGHTTP2_TOKEN_EXPECT = 34, + NGHTTP2_TOKEN_EXPIRES = 35, + NGHTTP2_TOKEN_FROM = 36, + NGHTTP2_TOKEN_HOST = 37, + NGHTTP2_TOKEN_IF_MATCH = 38, + NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39, + NGHTTP2_TOKEN_IF_NONE_MATCH = 40, + NGHTTP2_TOKEN_IF_RANGE = 41, + NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42, + NGHTTP2_TOKEN_LAST_MODIFIED = 43, + NGHTTP2_TOKEN_LINK = 44, + NGHTTP2_TOKEN_LOCATION = 45, + NGHTTP2_TOKEN_MAX_FORWARDS = 46, + NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47, + NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48, + NGHTTP2_TOKEN_RANGE = 49, + NGHTTP2_TOKEN_REFERER = 50, + NGHTTP2_TOKEN_REFRESH = 51, + NGHTTP2_TOKEN_RETRY_AFTER = 52, + NGHTTP2_TOKEN_SERVER = 53, + NGHTTP2_TOKEN_SET_COOKIE = 54, + NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55, + NGHTTP2_TOKEN_TRANSFER_ENCODING = 56, + NGHTTP2_TOKEN_USER_AGENT = 57, + NGHTTP2_TOKEN_VARY = 58, + NGHTTP2_TOKEN_VIA = 59, + NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, + NGHTTP2_TOKEN_TE, + NGHTTP2_TOKEN_CONNECTION, + NGHTTP2_TOKEN_KEEP_ALIVE, + NGHTTP2_TOKEN_PROXY_CONNECTION, + NGHTTP2_TOKEN_UPGRADE, + NGHTTP2_TOKEN__PROTOCOL, +} nghttp2_token; + +struct nghttp2_hd_entry; +typedef struct nghttp2_hd_entry nghttp2_hd_entry; + +typedef struct { + /* The buffer containing header field name. NULL-termination is + guaranteed. */ + nghttp2_rcbuf *name; + /* The buffer containing header field value. NULL-termination is + guaranteed. */ + nghttp2_rcbuf *value; + /* nghttp2_token value for name. It could be -1 if we have no token + for that header field name. */ + int32_t token; + /* Bitwise OR of one or more of nghttp2_nv_flag. */ + uint8_t flags; +} nghttp2_hd_nv; + +struct nghttp2_hd_entry { + /* The header field name/value pair */ + nghttp2_hd_nv nv; + /* This is solely for nghttp2_hd_{deflate,inflate}_get_table_entry + APIs to keep backward compatibility. */ + nghttp2_nv cnv; + /* The next entry which shares same bucket in hash table. */ + nghttp2_hd_entry *next; + /* The sequence number. We will increment it by one whenever we + store nghttp2_hd_entry to dynamic header table. */ + uint32_t seq; + /* The hash value for header name (nv.name). */ + uint32_t hash; +}; + +/* The entry used for static header table. */ +typedef struct { + nghttp2_rcbuf name; + nghttp2_rcbuf value; + nghttp2_nv cnv; + int32_t token; + uint32_t hash; +} nghttp2_hd_static_entry; + +typedef struct { + nghttp2_hd_entry **buffer; + size_t mask; + size_t first; + size_t len; +} nghttp2_hd_ringbuf; + +typedef enum { + NGHTTP2_HD_OPCODE_NONE, + NGHTTP2_HD_OPCODE_INDEXED, + NGHTTP2_HD_OPCODE_NEWNAME, + NGHTTP2_HD_OPCODE_INDNAME +} nghttp2_hd_opcode; + +typedef enum { + NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE, + NGHTTP2_HD_STATE_INFLATE_START, + NGHTTP2_HD_STATE_OPCODE, + NGHTTP2_HD_STATE_READ_TABLE_SIZE, + NGHTTP2_HD_STATE_READ_INDEX, + NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN, + NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN, + NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF, + NGHTTP2_HD_STATE_NEWNAME_READ_NAME, + NGHTTP2_HD_STATE_CHECK_VALUELEN, + NGHTTP2_HD_STATE_READ_VALUELEN, + NGHTTP2_HD_STATE_READ_VALUEHUFF, + NGHTTP2_HD_STATE_READ_VALUE +} nghttp2_hd_inflate_state; + +typedef enum { + NGHTTP2_HD_WITH_INDEXING, + NGHTTP2_HD_WITHOUT_INDEXING, + NGHTTP2_HD_NEVER_INDEXING +} nghttp2_hd_indexing_mode; + +typedef struct { + /* dynamic header table */ + nghttp2_hd_ringbuf hd_table; + /* Memory allocator */ + nghttp2_mem *mem; + /* Abstract buffer size of hd_table as described in the spec. This + is the sum of length of name/value in hd_table + + NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */ + size_t hd_table_bufsize; + /* The effective header table size. */ + size_t hd_table_bufsize_max; + /* Next sequence number for nghttp2_hd_entry */ + uint32_t next_seq; + /* If inflate/deflate error occurred, this value is set to 1 and + further invocation of inflate/deflate will fail with + NGHTTP2_ERR_HEADER_COMP. */ + uint8_t bad; +} nghttp2_hd_context; + +#define HD_MAP_SIZE 128 + +typedef struct { + nghttp2_hd_entry *table[HD_MAP_SIZE]; +} nghttp2_hd_map; + +struct nghttp2_hd_deflater { + nghttp2_hd_context ctx; + nghttp2_hd_map map; + /* The upper limit of the header table size the deflater accepts. */ + size_t deflate_hd_table_bufsize_max; + /* Minimum header table size notified in the next context update */ + size_t min_hd_table_bufsize_max; + /* If nonzero, send header table size using encoding context update + in the next deflate process */ + uint8_t notify_table_size_change; +}; + +struct nghttp2_hd_inflater { + nghttp2_hd_context ctx; + /* Stores current state of huffman decoding */ + nghttp2_hd_huff_decode_context huff_decode_ctx; + /* header buffer */ + nghttp2_buf namebuf, valuebuf; + nghttp2_rcbuf *namercbuf, *valuercbuf; + /* Pointer to the name/value pair which are used in the current + header emission. */ + nghttp2_rcbuf *nv_name_keep, *nv_value_keep; + /* The number of bytes to read */ + size_t left; + /* The index in indexed repr or indexed name */ + size_t index; + /* The maximum header table size the inflater supports. This is the + same value transmitted in SETTINGS_HEADER_TABLE_SIZE */ + size_t settings_hd_table_bufsize_max; + /* Minimum header table size set by nghttp2_hd_inflate_change_table_size */ + size_t min_hd_table_bufsize_max; + /* The number of next shift to decode integer */ + size_t shift; + nghttp2_hd_opcode opcode; + nghttp2_hd_inflate_state state; + /* nonzero if string is huffman encoded */ + uint8_t huffman_encoded; + /* nonzero if deflater requires that current entry is indexed */ + uint8_t index_required; + /* nonzero if deflater requires that current entry must not be + indexed */ + uint8_t no_index; +}; + +/* + * Initializes the |ent| members. The reference counts of nv->name + * and nv->value are increased by one for each. + */ +void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv); + +/* + * This function decreases the reference counts of nv->name and + * nv->value. + */ +void nghttp2_hd_entry_free(nghttp2_hd_entry *ent); + +/* + * Initializes |deflater| for deflating name/values pairs. + * + * The encoder only uses up to + * NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE bytes for header table + * even if the larger value is specified later in + * nghttp2_hd_change_table_size(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem); + +/* + * Initializes |deflater| for deflating name/values pairs. + * + * The encoder only uses up to |max_deflate_dynamic_table_size| bytes + * for header table even if the larger value is specified later in + * nghttp2_hd_change_table_size(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, + size_t max_deflate_dynamic_table_size, + nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |deflater|. + */ +void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater); + +/* + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |bufs|. + * + * This function expands |bufs| as necessary to store the result. If + * buffers is full and the process still requires more space, this + * function fails and returns NGHTTP2_ERR_HEADER_COMP. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_HEADER_COMP + * Deflation process has failed. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, const nghttp2_nv *nva, + size_t nvlen); + +/* + * Initializes |inflater| for inflating name/values pairs. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |inflater|. + */ +void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); + +/* + * Similar to nghttp2_hd_inflate_hd(), but this takes nghttp2_hd_nv + * instead of nghttp2_nv as output parameter |nv_out|. Other than + * that return values and semantics are the same as + * nghttp2_hd_inflate_hd(). + */ +ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, int in_final); + +/* For unittesting purpose */ +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, + nghttp2_nv *nv, int indexing_mode); + +/* For unittesting purpose */ +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, + int indexing_mode); + +/* For unittesting purpose */ +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); + +/* For unittesting purpose */ +nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index); + +/* For unittesting purpose */ +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix); + +/* Huffman encoding/decoding functions */ + +/* + * Counts the required bytes to encode |src| with length |len|. + * + * This function returns the number of required bytes to encode given + * data, including padding of prefix of terminal symbol code. This + * function always succeeds. + */ +size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len); + +/* + * Encodes the given data |src| with length |srclen| to the |bufs|. + * This function expands extra buffers in |bufs| if necessary. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen); + +void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); + +/* + * Decodes the given data |src| with length |srclen|. The |ctx| must + * be initialized by nghttp2_hd_huff_decode_context_init(). The result + * will be written to |buf|. This function assumes that |buf| has the + * enough room to store the decoded byte string. + * + * The caller must set the |fin| to nonzero if the given input is the + * final block. + * + * This function returns the number of read bytes from the |in|. + * + * If this function fails, it returns one of the following negative + * return codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_HEADER_COMP + * Decoding process has failed. + */ +ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_buf *buf, const uint8_t *src, + size_t srclen, int fin); + +/* + * nghttp2_hd_huff_decode_failure_state returns nonzero if |ctx| + * indicates that huffman decoding context is in failure state. + */ +int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx); + +#endif /* NGHTTP2_HD_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_hd_huffman.c b/Utilities/cmnghttp2/lib/nghttp2_hd_huffman.c new file mode 100644 index 00000000000..ac90f49c44f --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_hd_huffman.c @@ -0,0 +1,144 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd_huffman.h" + +#include +#include +#include + +#include "nghttp2_hd.h" +#include "nghttp2_net.h" + +size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { + size_t i; + size_t nbits = 0; + + for (i = 0; i < len; ++i) { + nbits += huff_sym_table[src[i]].nbits; + } + /* pad the prefix of EOS (256) */ + return (nbits + 7) / 8; +} + +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen) { + const nghttp2_huff_sym *sym; + const uint8_t *end = src + srclen; + uint64_t code = 0; + uint32_t x; + size_t nbits = 0; + size_t avail; + int rv; + + avail = nghttp2_bufs_cur_avail(bufs); + + for (; src != end;) { + sym = &huff_sym_table[*src++]; + code |= (uint64_t)sym->code << (32 - nbits); + nbits += sym->nbits; + if (nbits < 32) { + continue; + } + if (avail >= 4) { + x = htonl((uint32_t)(code >> 32)); + memcpy(bufs->cur->buf.last, &x, 4); + bufs->cur->buf.last += 4; + avail -= 4; + code <<= 32; + nbits -= 32; + continue; + } + + for (; nbits >= 8;) { + rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); + if (rv != 0) { + return rv; + } + code <<= 8; + nbits -= 8; + } + + avail = nghttp2_bufs_cur_avail(bufs); + } + + for (; nbits >= 8;) { + rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); + if (rv != 0) { + return rv; + } + code <<= 8; + nbits -= 8; + } + + if (nbits) { + rv = nghttp2_bufs_addb( + bufs, (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1))); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { + ctx->fstate = NGHTTP2_HUFF_ACCEPTED; +} + +ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_buf *buf, const uint8_t *src, + size_t srclen, int final) { + const uint8_t *end = src + srclen; + nghttp2_huff_decode node = {ctx->fstate, 0}; + const nghttp2_huff_decode *t = &node; + uint8_t c; + + /* We use the decoding algorithm described in + http://graphics.ics.uci.edu/pub/Prefix.pdf */ + for (; src != end;) { + c = *src++; + t = &huff_decode_table[t->fstate & 0x1ff][c >> 4]; + if (t->fstate & NGHTTP2_HUFF_SYM) { + *buf->last++ = t->sym; + } + + t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf]; + if (t->fstate & NGHTTP2_HUFF_SYM) { + *buf->last++ = t->sym; + } + } + + ctx->fstate = t->fstate; + + if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) { + return NGHTTP2_ERR_HEADER_COMP; + } + + return (ssize_t)srclen; +} + +int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx) { + return ctx->fstate == 0x100; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_hd_huffman.h b/Utilities/cmnghttp2/lib/nghttp2_hd_huffman.h new file mode 100644 index 00000000000..2bfd5318165 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_hd_huffman.h @@ -0,0 +1,72 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HD_HUFFMAN_H +#define NGHTTP2_HD_HUFFMAN_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef enum { + /* FSA accepts this state as the end of huffman encoding + sequence. */ + NGHTTP2_HUFF_ACCEPTED = 1 << 14, + /* This state emits symbol */ + NGHTTP2_HUFF_SYM = 1 << 15, +} nghttp2_huff_decode_flag; + +typedef struct { + /* fstate is the current huffman decoding state, which is actually + the node ID of internal huffman tree with + nghttp2_huff_decode_flag OR-ed. We have 257 leaf nodes, but they + are identical to root node other than emitting a symbol, so we + have 256 internal nodes [1..255], inclusive. The node ID 256 is + a special node and it is a terminal state that means decoding + failed. */ + uint16_t fstate; + /* symbol if NGHTTP2_HUFF_SYM flag set */ + uint8_t sym; +} nghttp2_huff_decode; + +typedef nghttp2_huff_decode huff_decode_table_type[16]; + +typedef struct { + /* fstate is the current huffman decoding state. */ + uint16_t fstate; +} nghttp2_hd_huff_decode_context; + +typedef struct { + /* The number of bits in this code */ + uint32_t nbits; + /* Huffman code aligned to LSB */ + uint32_t code; +} nghttp2_huff_sym; + +extern const nghttp2_huff_sym huff_sym_table[]; +extern const nghttp2_huff_decode huff_decode_table[][16]; + +#endif /* NGHTTP2_HD_HUFFMAN_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_hd_huffman_data.c b/Utilities/cmnghttp2/lib/nghttp2_hd_huffman_data.c new file mode 100644 index 00000000000..2e2e13f7bee --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_hd_huffman_data.c @@ -0,0 +1,4980 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd_huffman.h" + +/* Generated by mkhufftbl.py */ + +const nghttp2_huff_sym huff_sym_table[] = { + {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, + {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, + {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, + {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u}, + {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u}, + {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u}, + {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u}, + {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u}, + {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u}, + {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u}, + {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u}, + {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u}, + {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u}, + {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u}, + {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u}, + {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u}, + {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u}, + {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u}, + {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u}, + {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u}, + {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u}, + {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u}, + {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u}, + {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u}, + {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u}, + {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u}, + {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u}, + {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u}, + {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u}, + {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u}, + {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u}, + {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u}, + {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u}, + {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u}, + {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u}, + {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u}, + {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u}, + {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u}, + {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u}, + {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u}, + {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u}, + {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u}, + {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u}, + {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u}, + {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u}, + {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u}, + {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u}, + {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u}, + {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u}, + {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u}, + {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u}, + {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u}, + {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u}, + {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u}, + {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u}, + {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u}, + {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u}, + {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u}, + {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u}, + {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u}, + {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u}, + {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u}, + {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, + {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, + {30, 0xfffffffcu}}; + +const nghttp2_huff_decode huff_decode_table[][16] = { + /* 0 */ + { + {0x04, 0}, + {0x05, 0}, + {0x07, 0}, + {0x08, 0}, + {0x0b, 0}, + {0x0c, 0}, + {0x10, 0}, + {0x13, 0}, + {0x19, 0}, + {0x1c, 0}, + {0x20, 0}, + {0x23, 0}, + {0x2a, 0}, + {0x31, 0}, + {0x39, 0}, + {0x4040, 0}, + }, + /* 1 */ + { + {0xc000, 48}, + {0xc000, 49}, + {0xc000, 50}, + {0xc000, 97}, + {0xc000, 99}, + {0xc000, 101}, + {0xc000, 105}, + {0xc000, 111}, + {0xc000, 115}, + {0xc000, 116}, + {0x0d, 0}, + {0x0e, 0}, + {0x11, 0}, + {0x12, 0}, + {0x14, 0}, + {0x15, 0}, + }, + /* 2 */ + { + {0x8001, 48}, + {0xc016, 48}, + {0x8001, 49}, + {0xc016, 49}, + {0x8001, 50}, + {0xc016, 50}, + {0x8001, 97}, + {0xc016, 97}, + {0x8001, 99}, + {0xc016, 99}, + {0x8001, 101}, + {0xc016, 101}, + {0x8001, 105}, + {0xc016, 105}, + {0x8001, 111}, + {0xc016, 111}, + }, + /* 3 */ + { + {0x8002, 48}, + {0x8009, 48}, + {0x8017, 48}, + {0xc028, 48}, + {0x8002, 49}, + {0x8009, 49}, + {0x8017, 49}, + {0xc028, 49}, + {0x8002, 50}, + {0x8009, 50}, + {0x8017, 50}, + {0xc028, 50}, + {0x8002, 97}, + {0x8009, 97}, + {0x8017, 97}, + {0xc028, 97}, + }, + /* 4 */ + { + {0x8003, 48}, + {0x8006, 48}, + {0x800a, 48}, + {0x800f, 48}, + {0x8018, 48}, + {0x801f, 48}, + {0x8029, 48}, + {0xc038, 48}, + {0x8003, 49}, + {0x8006, 49}, + {0x800a, 49}, + {0x800f, 49}, + {0x8018, 49}, + {0x801f, 49}, + {0x8029, 49}, + {0xc038, 49}, + }, + /* 5 */ + { + {0x8003, 50}, + {0x8006, 50}, + {0x800a, 50}, + {0x800f, 50}, + {0x8018, 50}, + {0x801f, 50}, + {0x8029, 50}, + {0xc038, 50}, + {0x8003, 97}, + {0x8006, 97}, + {0x800a, 97}, + {0x800f, 97}, + {0x8018, 97}, + {0x801f, 97}, + {0x8029, 97}, + {0xc038, 97}, + }, + /* 6 */ + { + {0x8002, 99}, + {0x8009, 99}, + {0x8017, 99}, + {0xc028, 99}, + {0x8002, 101}, + {0x8009, 101}, + {0x8017, 101}, + {0xc028, 101}, + {0x8002, 105}, + {0x8009, 105}, + {0x8017, 105}, + {0xc028, 105}, + {0x8002, 111}, + {0x8009, 111}, + {0x8017, 111}, + {0xc028, 111}, + }, + /* 7 */ + { + {0x8003, 99}, + {0x8006, 99}, + {0x800a, 99}, + {0x800f, 99}, + {0x8018, 99}, + {0x801f, 99}, + {0x8029, 99}, + {0xc038, 99}, + {0x8003, 101}, + {0x8006, 101}, + {0x800a, 101}, + {0x800f, 101}, + {0x8018, 101}, + {0x801f, 101}, + {0x8029, 101}, + {0xc038, 101}, + }, + /* 8 */ + { + {0x8003, 105}, + {0x8006, 105}, + {0x800a, 105}, + {0x800f, 105}, + {0x8018, 105}, + {0x801f, 105}, + {0x8029, 105}, + {0xc038, 105}, + {0x8003, 111}, + {0x8006, 111}, + {0x800a, 111}, + {0x800f, 111}, + {0x8018, 111}, + {0x801f, 111}, + {0x8029, 111}, + {0xc038, 111}, + }, + /* 9 */ + { + {0x8001, 115}, + {0xc016, 115}, + {0x8001, 116}, + {0xc016, 116}, + {0xc000, 32}, + {0xc000, 37}, + {0xc000, 45}, + {0xc000, 46}, + {0xc000, 47}, + {0xc000, 51}, + {0xc000, 52}, + {0xc000, 53}, + {0xc000, 54}, + {0xc000, 55}, + {0xc000, 56}, + {0xc000, 57}, + }, + /* 10 */ + { + {0x8002, 115}, + {0x8009, 115}, + {0x8017, 115}, + {0xc028, 115}, + {0x8002, 116}, + {0x8009, 116}, + {0x8017, 116}, + {0xc028, 116}, + {0x8001, 32}, + {0xc016, 32}, + {0x8001, 37}, + {0xc016, 37}, + {0x8001, 45}, + {0xc016, 45}, + {0x8001, 46}, + {0xc016, 46}, + }, + /* 11 */ + { + {0x8003, 115}, + {0x8006, 115}, + {0x800a, 115}, + {0x800f, 115}, + {0x8018, 115}, + {0x801f, 115}, + {0x8029, 115}, + {0xc038, 115}, + {0x8003, 116}, + {0x8006, 116}, + {0x800a, 116}, + {0x800f, 116}, + {0x8018, 116}, + {0x801f, 116}, + {0x8029, 116}, + {0xc038, 116}, + }, + /* 12 */ + { + {0x8002, 32}, + {0x8009, 32}, + {0x8017, 32}, + {0xc028, 32}, + {0x8002, 37}, + {0x8009, 37}, + {0x8017, 37}, + {0xc028, 37}, + {0x8002, 45}, + {0x8009, 45}, + {0x8017, 45}, + {0xc028, 45}, + {0x8002, 46}, + {0x8009, 46}, + {0x8017, 46}, + {0xc028, 46}, + }, + /* 13 */ + { + {0x8003, 32}, + {0x8006, 32}, + {0x800a, 32}, + {0x800f, 32}, + {0x8018, 32}, + {0x801f, 32}, + {0x8029, 32}, + {0xc038, 32}, + {0x8003, 37}, + {0x8006, 37}, + {0x800a, 37}, + {0x800f, 37}, + {0x8018, 37}, + {0x801f, 37}, + {0x8029, 37}, + {0xc038, 37}, + }, + /* 14 */ + { + {0x8003, 45}, + {0x8006, 45}, + {0x800a, 45}, + {0x800f, 45}, + {0x8018, 45}, + {0x801f, 45}, + {0x8029, 45}, + {0xc038, 45}, + {0x8003, 46}, + {0x8006, 46}, + {0x800a, 46}, + {0x800f, 46}, + {0x8018, 46}, + {0x801f, 46}, + {0x8029, 46}, + {0xc038, 46}, + }, + /* 15 */ + { + {0x8001, 47}, + {0xc016, 47}, + {0x8001, 51}, + {0xc016, 51}, + {0x8001, 52}, + {0xc016, 52}, + {0x8001, 53}, + {0xc016, 53}, + {0x8001, 54}, + {0xc016, 54}, + {0x8001, 55}, + {0xc016, 55}, + {0x8001, 56}, + {0xc016, 56}, + {0x8001, 57}, + {0xc016, 57}, + }, + /* 16 */ + { + {0x8002, 47}, + {0x8009, 47}, + {0x8017, 47}, + {0xc028, 47}, + {0x8002, 51}, + {0x8009, 51}, + {0x8017, 51}, + {0xc028, 51}, + {0x8002, 52}, + {0x8009, 52}, + {0x8017, 52}, + {0xc028, 52}, + {0x8002, 53}, + {0x8009, 53}, + {0x8017, 53}, + {0xc028, 53}, + }, + /* 17 */ + { + {0x8003, 47}, + {0x8006, 47}, + {0x800a, 47}, + {0x800f, 47}, + {0x8018, 47}, + {0x801f, 47}, + {0x8029, 47}, + {0xc038, 47}, + {0x8003, 51}, + {0x8006, 51}, + {0x800a, 51}, + {0x800f, 51}, + {0x8018, 51}, + {0x801f, 51}, + {0x8029, 51}, + {0xc038, 51}, + }, + /* 18 */ + { + {0x8003, 52}, + {0x8006, 52}, + {0x800a, 52}, + {0x800f, 52}, + {0x8018, 52}, + {0x801f, 52}, + {0x8029, 52}, + {0xc038, 52}, + {0x8003, 53}, + {0x8006, 53}, + {0x800a, 53}, + {0x800f, 53}, + {0x8018, 53}, + {0x801f, 53}, + {0x8029, 53}, + {0xc038, 53}, + }, + /* 19 */ + { + {0x8002, 54}, + {0x8009, 54}, + {0x8017, 54}, + {0xc028, 54}, + {0x8002, 55}, + {0x8009, 55}, + {0x8017, 55}, + {0xc028, 55}, + {0x8002, 56}, + {0x8009, 56}, + {0x8017, 56}, + {0xc028, 56}, + {0x8002, 57}, + {0x8009, 57}, + {0x8017, 57}, + {0xc028, 57}, + }, + /* 20 */ + { + {0x8003, 54}, + {0x8006, 54}, + {0x800a, 54}, + {0x800f, 54}, + {0x8018, 54}, + {0x801f, 54}, + {0x8029, 54}, + {0xc038, 54}, + {0x8003, 55}, + {0x8006, 55}, + {0x800a, 55}, + {0x800f, 55}, + {0x8018, 55}, + {0x801f, 55}, + {0x8029, 55}, + {0xc038, 55}, + }, + /* 21 */ + { + {0x8003, 56}, + {0x8006, 56}, + {0x800a, 56}, + {0x800f, 56}, + {0x8018, 56}, + {0x801f, 56}, + {0x8029, 56}, + {0xc038, 56}, + {0x8003, 57}, + {0x8006, 57}, + {0x800a, 57}, + {0x800f, 57}, + {0x8018, 57}, + {0x801f, 57}, + {0x8029, 57}, + {0xc038, 57}, + }, + /* 22 */ + { + {0x1a, 0}, + {0x1b, 0}, + {0x1d, 0}, + {0x1e, 0}, + {0x21, 0}, + {0x22, 0}, + {0x24, 0}, + {0x25, 0}, + {0x2b, 0}, + {0x2e, 0}, + {0x32, 0}, + {0x35, 0}, + {0x3a, 0}, + {0x3d, 0}, + {0x41, 0}, + {0x4044, 0}, + }, + /* 23 */ + { + {0xc000, 61}, + {0xc000, 65}, + {0xc000, 95}, + {0xc000, 98}, + {0xc000, 100}, + {0xc000, 102}, + {0xc000, 103}, + {0xc000, 104}, + {0xc000, 108}, + {0xc000, 109}, + {0xc000, 110}, + {0xc000, 112}, + {0xc000, 114}, + {0xc000, 117}, + {0x26, 0}, + {0x27, 0}, + }, + /* 24 */ + { + {0x8001, 61}, + {0xc016, 61}, + {0x8001, 65}, + {0xc016, 65}, + {0x8001, 95}, + {0xc016, 95}, + {0x8001, 98}, + {0xc016, 98}, + {0x8001, 100}, + {0xc016, 100}, + {0x8001, 102}, + {0xc016, 102}, + {0x8001, 103}, + {0xc016, 103}, + {0x8001, 104}, + {0xc016, 104}, + }, + /* 25 */ + { + {0x8002, 61}, + {0x8009, 61}, + {0x8017, 61}, + {0xc028, 61}, + {0x8002, 65}, + {0x8009, 65}, + {0x8017, 65}, + {0xc028, 65}, + {0x8002, 95}, + {0x8009, 95}, + {0x8017, 95}, + {0xc028, 95}, + {0x8002, 98}, + {0x8009, 98}, + {0x8017, 98}, + {0xc028, 98}, + }, + /* 26 */ + { + {0x8003, 61}, + {0x8006, 61}, + {0x800a, 61}, + {0x800f, 61}, + {0x8018, 61}, + {0x801f, 61}, + {0x8029, 61}, + {0xc038, 61}, + {0x8003, 65}, + {0x8006, 65}, + {0x800a, 65}, + {0x800f, 65}, + {0x8018, 65}, + {0x801f, 65}, + {0x8029, 65}, + {0xc038, 65}, + }, + /* 27 */ + { + {0x8003, 95}, + {0x8006, 95}, + {0x800a, 95}, + {0x800f, 95}, + {0x8018, 95}, + {0x801f, 95}, + {0x8029, 95}, + {0xc038, 95}, + {0x8003, 98}, + {0x8006, 98}, + {0x800a, 98}, + {0x800f, 98}, + {0x8018, 98}, + {0x801f, 98}, + {0x8029, 98}, + {0xc038, 98}, + }, + /* 28 */ + { + {0x8002, 100}, + {0x8009, 100}, + {0x8017, 100}, + {0xc028, 100}, + {0x8002, 102}, + {0x8009, 102}, + {0x8017, 102}, + {0xc028, 102}, + {0x8002, 103}, + {0x8009, 103}, + {0x8017, 103}, + {0xc028, 103}, + {0x8002, 104}, + {0x8009, 104}, + {0x8017, 104}, + {0xc028, 104}, + }, + /* 29 */ + { + {0x8003, 100}, + {0x8006, 100}, + {0x800a, 100}, + {0x800f, 100}, + {0x8018, 100}, + {0x801f, 100}, + {0x8029, 100}, + {0xc038, 100}, + {0x8003, 102}, + {0x8006, 102}, + {0x800a, 102}, + {0x800f, 102}, + {0x8018, 102}, + {0x801f, 102}, + {0x8029, 102}, + {0xc038, 102}, + }, + /* 30 */ + { + {0x8003, 103}, + {0x8006, 103}, + {0x800a, 103}, + {0x800f, 103}, + {0x8018, 103}, + {0x801f, 103}, + {0x8029, 103}, + {0xc038, 103}, + {0x8003, 104}, + {0x8006, 104}, + {0x800a, 104}, + {0x800f, 104}, + {0x8018, 104}, + {0x801f, 104}, + {0x8029, 104}, + {0xc038, 104}, + }, + /* 31 */ + { + {0x8001, 108}, + {0xc016, 108}, + {0x8001, 109}, + {0xc016, 109}, + {0x8001, 110}, + {0xc016, 110}, + {0x8001, 112}, + {0xc016, 112}, + {0x8001, 114}, + {0xc016, 114}, + {0x8001, 117}, + {0xc016, 117}, + {0xc000, 58}, + {0xc000, 66}, + {0xc000, 67}, + {0xc000, 68}, + }, + /* 32 */ + { + {0x8002, 108}, + {0x8009, 108}, + {0x8017, 108}, + {0xc028, 108}, + {0x8002, 109}, + {0x8009, 109}, + {0x8017, 109}, + {0xc028, 109}, + {0x8002, 110}, + {0x8009, 110}, + {0x8017, 110}, + {0xc028, 110}, + {0x8002, 112}, + {0x8009, 112}, + {0x8017, 112}, + {0xc028, 112}, + }, + /* 33 */ + { + {0x8003, 108}, + {0x8006, 108}, + {0x800a, 108}, + {0x800f, 108}, + {0x8018, 108}, + {0x801f, 108}, + {0x8029, 108}, + {0xc038, 108}, + {0x8003, 109}, + {0x8006, 109}, + {0x800a, 109}, + {0x800f, 109}, + {0x8018, 109}, + {0x801f, 109}, + {0x8029, 109}, + {0xc038, 109}, + }, + /* 34 */ + { + {0x8003, 110}, + {0x8006, 110}, + {0x800a, 110}, + {0x800f, 110}, + {0x8018, 110}, + {0x801f, 110}, + {0x8029, 110}, + {0xc038, 110}, + {0x8003, 112}, + {0x8006, 112}, + {0x800a, 112}, + {0x800f, 112}, + {0x8018, 112}, + {0x801f, 112}, + {0x8029, 112}, + {0xc038, 112}, + }, + /* 35 */ + { + {0x8002, 114}, + {0x8009, 114}, + {0x8017, 114}, + {0xc028, 114}, + {0x8002, 117}, + {0x8009, 117}, + {0x8017, 117}, + {0xc028, 117}, + {0x8001, 58}, + {0xc016, 58}, + {0x8001, 66}, + {0xc016, 66}, + {0x8001, 67}, + {0xc016, 67}, + {0x8001, 68}, + {0xc016, 68}, + }, + /* 36 */ + { + {0x8003, 114}, + {0x8006, 114}, + {0x800a, 114}, + {0x800f, 114}, + {0x8018, 114}, + {0x801f, 114}, + {0x8029, 114}, + {0xc038, 114}, + {0x8003, 117}, + {0x8006, 117}, + {0x800a, 117}, + {0x800f, 117}, + {0x8018, 117}, + {0x801f, 117}, + {0x8029, 117}, + {0xc038, 117}, + }, + /* 37 */ + { + {0x8002, 58}, + {0x8009, 58}, + {0x8017, 58}, + {0xc028, 58}, + {0x8002, 66}, + {0x8009, 66}, + {0x8017, 66}, + {0xc028, 66}, + {0x8002, 67}, + {0x8009, 67}, + {0x8017, 67}, + {0xc028, 67}, + {0x8002, 68}, + {0x8009, 68}, + {0x8017, 68}, + {0xc028, 68}, + }, + /* 38 */ + { + {0x8003, 58}, + {0x8006, 58}, + {0x800a, 58}, + {0x800f, 58}, + {0x8018, 58}, + {0x801f, 58}, + {0x8029, 58}, + {0xc038, 58}, + {0x8003, 66}, + {0x8006, 66}, + {0x800a, 66}, + {0x800f, 66}, + {0x8018, 66}, + {0x801f, 66}, + {0x8029, 66}, + {0xc038, 66}, + }, + /* 39 */ + { + {0x8003, 67}, + {0x8006, 67}, + {0x800a, 67}, + {0x800f, 67}, + {0x8018, 67}, + {0x801f, 67}, + {0x8029, 67}, + {0xc038, 67}, + {0x8003, 68}, + {0x8006, 68}, + {0x800a, 68}, + {0x800f, 68}, + {0x8018, 68}, + {0x801f, 68}, + {0x8029, 68}, + {0xc038, 68}, + }, + /* 40 */ + { + {0x2c, 0}, + {0x2d, 0}, + {0x2f, 0}, + {0x30, 0}, + {0x33, 0}, + {0x34, 0}, + {0x36, 0}, + {0x37, 0}, + {0x3b, 0}, + {0x3c, 0}, + {0x3e, 0}, + {0x3f, 0}, + {0x42, 0}, + {0x43, 0}, + {0x45, 0}, + {0x4048, 0}, + }, + /* 41 */ + { + {0xc000, 69}, + {0xc000, 70}, + {0xc000, 71}, + {0xc000, 72}, + {0xc000, 73}, + {0xc000, 74}, + {0xc000, 75}, + {0xc000, 76}, + {0xc000, 77}, + {0xc000, 78}, + {0xc000, 79}, + {0xc000, 80}, + {0xc000, 81}, + {0xc000, 82}, + {0xc000, 83}, + {0xc000, 84}, + }, + /* 42 */ + { + {0x8001, 69}, + {0xc016, 69}, + {0x8001, 70}, + {0xc016, 70}, + {0x8001, 71}, + {0xc016, 71}, + {0x8001, 72}, + {0xc016, 72}, + {0x8001, 73}, + {0xc016, 73}, + {0x8001, 74}, + {0xc016, 74}, + {0x8001, 75}, + {0xc016, 75}, + {0x8001, 76}, + {0xc016, 76}, + }, + /* 43 */ + { + {0x8002, 69}, + {0x8009, 69}, + {0x8017, 69}, + {0xc028, 69}, + {0x8002, 70}, + {0x8009, 70}, + {0x8017, 70}, + {0xc028, 70}, + {0x8002, 71}, + {0x8009, 71}, + {0x8017, 71}, + {0xc028, 71}, + {0x8002, 72}, + {0x8009, 72}, + {0x8017, 72}, + {0xc028, 72}, + }, + /* 44 */ + { + {0x8003, 69}, + {0x8006, 69}, + {0x800a, 69}, + {0x800f, 69}, + {0x8018, 69}, + {0x801f, 69}, + {0x8029, 69}, + {0xc038, 69}, + {0x8003, 70}, + {0x8006, 70}, + {0x800a, 70}, + {0x800f, 70}, + {0x8018, 70}, + {0x801f, 70}, + {0x8029, 70}, + {0xc038, 70}, + }, + /* 45 */ + { + {0x8003, 71}, + {0x8006, 71}, + {0x800a, 71}, + {0x800f, 71}, + {0x8018, 71}, + {0x801f, 71}, + {0x8029, 71}, + {0xc038, 71}, + {0x8003, 72}, + {0x8006, 72}, + {0x800a, 72}, + {0x800f, 72}, + {0x8018, 72}, + {0x801f, 72}, + {0x8029, 72}, + {0xc038, 72}, + }, + /* 46 */ + { + {0x8002, 73}, + {0x8009, 73}, + {0x8017, 73}, + {0xc028, 73}, + {0x8002, 74}, + {0x8009, 74}, + {0x8017, 74}, + {0xc028, 74}, + {0x8002, 75}, + {0x8009, 75}, + {0x8017, 75}, + {0xc028, 75}, + {0x8002, 76}, + {0x8009, 76}, + {0x8017, 76}, + {0xc028, 76}, + }, + /* 47 */ + { + {0x8003, 73}, + {0x8006, 73}, + {0x800a, 73}, + {0x800f, 73}, + {0x8018, 73}, + {0x801f, 73}, + {0x8029, 73}, + {0xc038, 73}, + {0x8003, 74}, + {0x8006, 74}, + {0x800a, 74}, + {0x800f, 74}, + {0x8018, 74}, + {0x801f, 74}, + {0x8029, 74}, + {0xc038, 74}, + }, + /* 48 */ + { + {0x8003, 75}, + {0x8006, 75}, + {0x800a, 75}, + {0x800f, 75}, + {0x8018, 75}, + {0x801f, 75}, + {0x8029, 75}, + {0xc038, 75}, + {0x8003, 76}, + {0x8006, 76}, + {0x800a, 76}, + {0x800f, 76}, + {0x8018, 76}, + {0x801f, 76}, + {0x8029, 76}, + {0xc038, 76}, + }, + /* 49 */ + { + {0x8001, 77}, + {0xc016, 77}, + {0x8001, 78}, + {0xc016, 78}, + {0x8001, 79}, + {0xc016, 79}, + {0x8001, 80}, + {0xc016, 80}, + {0x8001, 81}, + {0xc016, 81}, + {0x8001, 82}, + {0xc016, 82}, + {0x8001, 83}, + {0xc016, 83}, + {0x8001, 84}, + {0xc016, 84}, + }, + /* 50 */ + { + {0x8002, 77}, + {0x8009, 77}, + {0x8017, 77}, + {0xc028, 77}, + {0x8002, 78}, + {0x8009, 78}, + {0x8017, 78}, + {0xc028, 78}, + {0x8002, 79}, + {0x8009, 79}, + {0x8017, 79}, + {0xc028, 79}, + {0x8002, 80}, + {0x8009, 80}, + {0x8017, 80}, + {0xc028, 80}, + }, + /* 51 */ + { + {0x8003, 77}, + {0x8006, 77}, + {0x800a, 77}, + {0x800f, 77}, + {0x8018, 77}, + {0x801f, 77}, + {0x8029, 77}, + {0xc038, 77}, + {0x8003, 78}, + {0x8006, 78}, + {0x800a, 78}, + {0x800f, 78}, + {0x8018, 78}, + {0x801f, 78}, + {0x8029, 78}, + {0xc038, 78}, + }, + /* 52 */ + { + {0x8003, 79}, + {0x8006, 79}, + {0x800a, 79}, + {0x800f, 79}, + {0x8018, 79}, + {0x801f, 79}, + {0x8029, 79}, + {0xc038, 79}, + {0x8003, 80}, + {0x8006, 80}, + {0x800a, 80}, + {0x800f, 80}, + {0x8018, 80}, + {0x801f, 80}, + {0x8029, 80}, + {0xc038, 80}, + }, + /* 53 */ + { + {0x8002, 81}, + {0x8009, 81}, + {0x8017, 81}, + {0xc028, 81}, + {0x8002, 82}, + {0x8009, 82}, + {0x8017, 82}, + {0xc028, 82}, + {0x8002, 83}, + {0x8009, 83}, + {0x8017, 83}, + {0xc028, 83}, + {0x8002, 84}, + {0x8009, 84}, + {0x8017, 84}, + {0xc028, 84}, + }, + /* 54 */ + { + {0x8003, 81}, + {0x8006, 81}, + {0x800a, 81}, + {0x800f, 81}, + {0x8018, 81}, + {0x801f, 81}, + {0x8029, 81}, + {0xc038, 81}, + {0x8003, 82}, + {0x8006, 82}, + {0x800a, 82}, + {0x800f, 82}, + {0x8018, 82}, + {0x801f, 82}, + {0x8029, 82}, + {0xc038, 82}, + }, + /* 55 */ + { + {0x8003, 83}, + {0x8006, 83}, + {0x800a, 83}, + {0x800f, 83}, + {0x8018, 83}, + {0x801f, 83}, + {0x8029, 83}, + {0xc038, 83}, + {0x8003, 84}, + {0x8006, 84}, + {0x800a, 84}, + {0x800f, 84}, + {0x8018, 84}, + {0x801f, 84}, + {0x8029, 84}, + {0xc038, 84}, + }, + /* 56 */ + { + {0xc000, 85}, + {0xc000, 86}, + {0xc000, 87}, + {0xc000, 89}, + {0xc000, 106}, + {0xc000, 107}, + {0xc000, 113}, + {0xc000, 118}, + {0xc000, 119}, + {0xc000, 120}, + {0xc000, 121}, + {0xc000, 122}, + {0x46, 0}, + {0x47, 0}, + {0x49, 0}, + {0x404a, 0}, + }, + /* 57 */ + { + {0x8001, 85}, + {0xc016, 85}, + {0x8001, 86}, + {0xc016, 86}, + {0x8001, 87}, + {0xc016, 87}, + {0x8001, 89}, + {0xc016, 89}, + {0x8001, 106}, + {0xc016, 106}, + {0x8001, 107}, + {0xc016, 107}, + {0x8001, 113}, + {0xc016, 113}, + {0x8001, 118}, + {0xc016, 118}, + }, + /* 58 */ + { + {0x8002, 85}, + {0x8009, 85}, + {0x8017, 85}, + {0xc028, 85}, + {0x8002, 86}, + {0x8009, 86}, + {0x8017, 86}, + {0xc028, 86}, + {0x8002, 87}, + {0x8009, 87}, + {0x8017, 87}, + {0xc028, 87}, + {0x8002, 89}, + {0x8009, 89}, + {0x8017, 89}, + {0xc028, 89}, + }, + /* 59 */ + { + {0x8003, 85}, + {0x8006, 85}, + {0x800a, 85}, + {0x800f, 85}, + {0x8018, 85}, + {0x801f, 85}, + {0x8029, 85}, + {0xc038, 85}, + {0x8003, 86}, + {0x8006, 86}, + {0x800a, 86}, + {0x800f, 86}, + {0x8018, 86}, + {0x801f, 86}, + {0x8029, 86}, + {0xc038, 86}, + }, + /* 60 */ + { + {0x8003, 87}, + {0x8006, 87}, + {0x800a, 87}, + {0x800f, 87}, + {0x8018, 87}, + {0x801f, 87}, + {0x8029, 87}, + {0xc038, 87}, + {0x8003, 89}, + {0x8006, 89}, + {0x800a, 89}, + {0x800f, 89}, + {0x8018, 89}, + {0x801f, 89}, + {0x8029, 89}, + {0xc038, 89}, + }, + /* 61 */ + { + {0x8002, 106}, + {0x8009, 106}, + {0x8017, 106}, + {0xc028, 106}, + {0x8002, 107}, + {0x8009, 107}, + {0x8017, 107}, + {0xc028, 107}, + {0x8002, 113}, + {0x8009, 113}, + {0x8017, 113}, + {0xc028, 113}, + {0x8002, 118}, + {0x8009, 118}, + {0x8017, 118}, + {0xc028, 118}, + }, + /* 62 */ + { + {0x8003, 106}, + {0x8006, 106}, + {0x800a, 106}, + {0x800f, 106}, + {0x8018, 106}, + {0x801f, 106}, + {0x8029, 106}, + {0xc038, 106}, + {0x8003, 107}, + {0x8006, 107}, + {0x800a, 107}, + {0x800f, 107}, + {0x8018, 107}, + {0x801f, 107}, + {0x8029, 107}, + {0xc038, 107}, + }, + /* 63 */ + { + {0x8003, 113}, + {0x8006, 113}, + {0x800a, 113}, + {0x800f, 113}, + {0x8018, 113}, + {0x801f, 113}, + {0x8029, 113}, + {0xc038, 113}, + {0x8003, 118}, + {0x8006, 118}, + {0x800a, 118}, + {0x800f, 118}, + {0x8018, 118}, + {0x801f, 118}, + {0x8029, 118}, + {0xc038, 118}, + }, + /* 64 */ + { + {0x8001, 119}, + {0xc016, 119}, + {0x8001, 120}, + {0xc016, 120}, + {0x8001, 121}, + {0xc016, 121}, + {0x8001, 122}, + {0xc016, 122}, + {0xc000, 38}, + {0xc000, 42}, + {0xc000, 44}, + {0xc000, 59}, + {0xc000, 88}, + {0xc000, 90}, + {0x4b, 0}, + {0x4e, 0}, + }, + /* 65 */ + { + {0x8002, 119}, + {0x8009, 119}, + {0x8017, 119}, + {0xc028, 119}, + {0x8002, 120}, + {0x8009, 120}, + {0x8017, 120}, + {0xc028, 120}, + {0x8002, 121}, + {0x8009, 121}, + {0x8017, 121}, + {0xc028, 121}, + {0x8002, 122}, + {0x8009, 122}, + {0x8017, 122}, + {0xc028, 122}, + }, + /* 66 */ + { + {0x8003, 119}, + {0x8006, 119}, + {0x800a, 119}, + {0x800f, 119}, + {0x8018, 119}, + {0x801f, 119}, + {0x8029, 119}, + {0xc038, 119}, + {0x8003, 120}, + {0x8006, 120}, + {0x800a, 120}, + {0x800f, 120}, + {0x8018, 120}, + {0x801f, 120}, + {0x8029, 120}, + {0xc038, 120}, + }, + /* 67 */ + { + {0x8003, 121}, + {0x8006, 121}, + {0x800a, 121}, + {0x800f, 121}, + {0x8018, 121}, + {0x801f, 121}, + {0x8029, 121}, + {0xc038, 121}, + {0x8003, 122}, + {0x8006, 122}, + {0x800a, 122}, + {0x800f, 122}, + {0x8018, 122}, + {0x801f, 122}, + {0x8029, 122}, + {0xc038, 122}, + }, + /* 68 */ + { + {0x8001, 38}, + {0xc016, 38}, + {0x8001, 42}, + {0xc016, 42}, + {0x8001, 44}, + {0xc016, 44}, + {0x8001, 59}, + {0xc016, 59}, + {0x8001, 88}, + {0xc016, 88}, + {0x8001, 90}, + {0xc016, 90}, + {0x4c, 0}, + {0x4d, 0}, + {0x4f, 0}, + {0x51, 0}, + }, + /* 69 */ + { + {0x8002, 38}, + {0x8009, 38}, + {0x8017, 38}, + {0xc028, 38}, + {0x8002, 42}, + {0x8009, 42}, + {0x8017, 42}, + {0xc028, 42}, + {0x8002, 44}, + {0x8009, 44}, + {0x8017, 44}, + {0xc028, 44}, + {0x8002, 59}, + {0x8009, 59}, + {0x8017, 59}, + {0xc028, 59}, + }, + /* 70 */ + { + {0x8003, 38}, + {0x8006, 38}, + {0x800a, 38}, + {0x800f, 38}, + {0x8018, 38}, + {0x801f, 38}, + {0x8029, 38}, + {0xc038, 38}, + {0x8003, 42}, + {0x8006, 42}, + {0x800a, 42}, + {0x800f, 42}, + {0x8018, 42}, + {0x801f, 42}, + {0x8029, 42}, + {0xc038, 42}, + }, + /* 71 */ + { + {0x8003, 44}, + {0x8006, 44}, + {0x800a, 44}, + {0x800f, 44}, + {0x8018, 44}, + {0x801f, 44}, + {0x8029, 44}, + {0xc038, 44}, + {0x8003, 59}, + {0x8006, 59}, + {0x800a, 59}, + {0x800f, 59}, + {0x8018, 59}, + {0x801f, 59}, + {0x8029, 59}, + {0xc038, 59}, + }, + /* 72 */ + { + {0x8002, 88}, + {0x8009, 88}, + {0x8017, 88}, + {0xc028, 88}, + {0x8002, 90}, + {0x8009, 90}, + {0x8017, 90}, + {0xc028, 90}, + {0xc000, 33}, + {0xc000, 34}, + {0xc000, 40}, + {0xc000, 41}, + {0xc000, 63}, + {0x50, 0}, + {0x52, 0}, + {0x54, 0}, + }, + /* 73 */ + { + {0x8003, 88}, + {0x8006, 88}, + {0x800a, 88}, + {0x800f, 88}, + {0x8018, 88}, + {0x801f, 88}, + {0x8029, 88}, + {0xc038, 88}, + {0x8003, 90}, + {0x8006, 90}, + {0x800a, 90}, + {0x800f, 90}, + {0x8018, 90}, + {0x801f, 90}, + {0x8029, 90}, + {0xc038, 90}, + }, + /* 74 */ + { + {0x8001, 33}, + {0xc016, 33}, + {0x8001, 34}, + {0xc016, 34}, + {0x8001, 40}, + {0xc016, 40}, + {0x8001, 41}, + {0xc016, 41}, + {0x8001, 63}, + {0xc016, 63}, + {0xc000, 39}, + {0xc000, 43}, + {0xc000, 124}, + {0x53, 0}, + {0x55, 0}, + {0x58, 0}, + }, + /* 75 */ + { + {0x8002, 33}, + {0x8009, 33}, + {0x8017, 33}, + {0xc028, 33}, + {0x8002, 34}, + {0x8009, 34}, + {0x8017, 34}, + {0xc028, 34}, + {0x8002, 40}, + {0x8009, 40}, + {0x8017, 40}, + {0xc028, 40}, + {0x8002, 41}, + {0x8009, 41}, + {0x8017, 41}, + {0xc028, 41}, + }, + /* 76 */ + { + {0x8003, 33}, + {0x8006, 33}, + {0x800a, 33}, + {0x800f, 33}, + {0x8018, 33}, + {0x801f, 33}, + {0x8029, 33}, + {0xc038, 33}, + {0x8003, 34}, + {0x8006, 34}, + {0x800a, 34}, + {0x800f, 34}, + {0x8018, 34}, + {0x801f, 34}, + {0x8029, 34}, + {0xc038, 34}, + }, + /* 77 */ + { + {0x8003, 40}, + {0x8006, 40}, + {0x800a, 40}, + {0x800f, 40}, + {0x8018, 40}, + {0x801f, 40}, + {0x8029, 40}, + {0xc038, 40}, + {0x8003, 41}, + {0x8006, 41}, + {0x800a, 41}, + {0x800f, 41}, + {0x8018, 41}, + {0x801f, 41}, + {0x8029, 41}, + {0xc038, 41}, + }, + /* 78 */ + { + {0x8002, 63}, + {0x8009, 63}, + {0x8017, 63}, + {0xc028, 63}, + {0x8001, 39}, + {0xc016, 39}, + {0x8001, 43}, + {0xc016, 43}, + {0x8001, 124}, + {0xc016, 124}, + {0xc000, 35}, + {0xc000, 62}, + {0x56, 0}, + {0x57, 0}, + {0x59, 0}, + {0x5a, 0}, + }, + /* 79 */ + { + {0x8003, 63}, + {0x8006, 63}, + {0x800a, 63}, + {0x800f, 63}, + {0x8018, 63}, + {0x801f, 63}, + {0x8029, 63}, + {0xc038, 63}, + {0x8002, 39}, + {0x8009, 39}, + {0x8017, 39}, + {0xc028, 39}, + {0x8002, 43}, + {0x8009, 43}, + {0x8017, 43}, + {0xc028, 43}, + }, + /* 80 */ + { + {0x8003, 39}, + {0x8006, 39}, + {0x800a, 39}, + {0x800f, 39}, + {0x8018, 39}, + {0x801f, 39}, + {0x8029, 39}, + {0xc038, 39}, + {0x8003, 43}, + {0x8006, 43}, + {0x800a, 43}, + {0x800f, 43}, + {0x8018, 43}, + {0x801f, 43}, + {0x8029, 43}, + {0xc038, 43}, + }, + /* 81 */ + { + {0x8002, 124}, + {0x8009, 124}, + {0x8017, 124}, + {0xc028, 124}, + {0x8001, 35}, + {0xc016, 35}, + {0x8001, 62}, + {0xc016, 62}, + {0xc000, 0}, + {0xc000, 36}, + {0xc000, 64}, + {0xc000, 91}, + {0xc000, 93}, + {0xc000, 126}, + {0x5b, 0}, + {0x5c, 0}, + }, + /* 82 */ + { + {0x8003, 124}, + {0x8006, 124}, + {0x800a, 124}, + {0x800f, 124}, + {0x8018, 124}, + {0x801f, 124}, + {0x8029, 124}, + {0xc038, 124}, + {0x8002, 35}, + {0x8009, 35}, + {0x8017, 35}, + {0xc028, 35}, + {0x8002, 62}, + {0x8009, 62}, + {0x8017, 62}, + {0xc028, 62}, + }, + /* 83 */ + { + {0x8003, 35}, + {0x8006, 35}, + {0x800a, 35}, + {0x800f, 35}, + {0x8018, 35}, + {0x801f, 35}, + {0x8029, 35}, + {0xc038, 35}, + {0x8003, 62}, + {0x8006, 62}, + {0x800a, 62}, + {0x800f, 62}, + {0x8018, 62}, + {0x801f, 62}, + {0x8029, 62}, + {0xc038, 62}, + }, + /* 84 */ + { + {0x8001, 0}, + {0xc016, 0}, + {0x8001, 36}, + {0xc016, 36}, + {0x8001, 64}, + {0xc016, 64}, + {0x8001, 91}, + {0xc016, 91}, + {0x8001, 93}, + {0xc016, 93}, + {0x8001, 126}, + {0xc016, 126}, + {0xc000, 94}, + {0xc000, 125}, + {0x5d, 0}, + {0x5e, 0}, + }, + /* 85 */ + { + {0x8002, 0}, + {0x8009, 0}, + {0x8017, 0}, + {0xc028, 0}, + {0x8002, 36}, + {0x8009, 36}, + {0x8017, 36}, + {0xc028, 36}, + {0x8002, 64}, + {0x8009, 64}, + {0x8017, 64}, + {0xc028, 64}, + {0x8002, 91}, + {0x8009, 91}, + {0x8017, 91}, + {0xc028, 91}, + }, + /* 86 */ + { + {0x8003, 0}, + {0x8006, 0}, + {0x800a, 0}, + {0x800f, 0}, + {0x8018, 0}, + {0x801f, 0}, + {0x8029, 0}, + {0xc038, 0}, + {0x8003, 36}, + {0x8006, 36}, + {0x800a, 36}, + {0x800f, 36}, + {0x8018, 36}, + {0x801f, 36}, + {0x8029, 36}, + {0xc038, 36}, + }, + /* 87 */ + { + {0x8003, 64}, + {0x8006, 64}, + {0x800a, 64}, + {0x800f, 64}, + {0x8018, 64}, + {0x801f, 64}, + {0x8029, 64}, + {0xc038, 64}, + {0x8003, 91}, + {0x8006, 91}, + {0x800a, 91}, + {0x800f, 91}, + {0x8018, 91}, + {0x801f, 91}, + {0x8029, 91}, + {0xc038, 91}, + }, + /* 88 */ + { + {0x8002, 93}, + {0x8009, 93}, + {0x8017, 93}, + {0xc028, 93}, + {0x8002, 126}, + {0x8009, 126}, + {0x8017, 126}, + {0xc028, 126}, + {0x8001, 94}, + {0xc016, 94}, + {0x8001, 125}, + {0xc016, 125}, + {0xc000, 60}, + {0xc000, 96}, + {0xc000, 123}, + {0x5f, 0}, + }, + /* 89 */ + { + {0x8003, 93}, + {0x8006, 93}, + {0x800a, 93}, + {0x800f, 93}, + {0x8018, 93}, + {0x801f, 93}, + {0x8029, 93}, + {0xc038, 93}, + {0x8003, 126}, + {0x8006, 126}, + {0x800a, 126}, + {0x800f, 126}, + {0x8018, 126}, + {0x801f, 126}, + {0x8029, 126}, + {0xc038, 126}, + }, + /* 90 */ + { + {0x8002, 94}, + {0x8009, 94}, + {0x8017, 94}, + {0xc028, 94}, + {0x8002, 125}, + {0x8009, 125}, + {0x8017, 125}, + {0xc028, 125}, + {0x8001, 60}, + {0xc016, 60}, + {0x8001, 96}, + {0xc016, 96}, + {0x8001, 123}, + {0xc016, 123}, + {0x60, 0}, + {0x6e, 0}, + }, + /* 91 */ + { + {0x8003, 94}, + {0x8006, 94}, + {0x800a, 94}, + {0x800f, 94}, + {0x8018, 94}, + {0x801f, 94}, + {0x8029, 94}, + {0xc038, 94}, + {0x8003, 125}, + {0x8006, 125}, + {0x800a, 125}, + {0x800f, 125}, + {0x8018, 125}, + {0x801f, 125}, + {0x8029, 125}, + {0xc038, 125}, + }, + /* 92 */ + { + {0x8002, 60}, + {0x8009, 60}, + {0x8017, 60}, + {0xc028, 60}, + {0x8002, 96}, + {0x8009, 96}, + {0x8017, 96}, + {0xc028, 96}, + {0x8002, 123}, + {0x8009, 123}, + {0x8017, 123}, + {0xc028, 123}, + {0x61, 0}, + {0x65, 0}, + {0x6f, 0}, + {0x85, 0}, + }, + /* 93 */ + { + {0x8003, 60}, + {0x8006, 60}, + {0x800a, 60}, + {0x800f, 60}, + {0x8018, 60}, + {0x801f, 60}, + {0x8029, 60}, + {0xc038, 60}, + {0x8003, 96}, + {0x8006, 96}, + {0x800a, 96}, + {0x800f, 96}, + {0x8018, 96}, + {0x801f, 96}, + {0x8029, 96}, + {0xc038, 96}, + }, + /* 94 */ + { + {0x8003, 123}, + {0x8006, 123}, + {0x800a, 123}, + {0x800f, 123}, + {0x8018, 123}, + {0x801f, 123}, + {0x8029, 123}, + {0xc038, 123}, + {0x62, 0}, + {0x63, 0}, + {0x66, 0}, + {0x69, 0}, + {0x70, 0}, + {0x77, 0}, + {0x86, 0}, + {0x99, 0}, + }, + /* 95 */ + { + {0xc000, 92}, + {0xc000, 195}, + {0xc000, 208}, + {0x64, 0}, + {0x67, 0}, + {0x68, 0}, + {0x6a, 0}, + {0x6b, 0}, + {0x71, 0}, + {0x74, 0}, + {0x78, 0}, + {0x7e, 0}, + {0x87, 0}, + {0x8e, 0}, + {0x9a, 0}, + {0xa9, 0}, + }, + /* 96 */ + { + {0x8001, 92}, + {0xc016, 92}, + {0x8001, 195}, + {0xc016, 195}, + {0x8001, 208}, + {0xc016, 208}, + {0xc000, 128}, + {0xc000, 130}, + {0xc000, 131}, + {0xc000, 162}, + {0xc000, 184}, + {0xc000, 194}, + {0xc000, 224}, + {0xc000, 226}, + {0x6c, 0}, + {0x6d, 0}, + }, + /* 97 */ + { + {0x8002, 92}, + {0x8009, 92}, + {0x8017, 92}, + {0xc028, 92}, + {0x8002, 195}, + {0x8009, 195}, + {0x8017, 195}, + {0xc028, 195}, + {0x8002, 208}, + {0x8009, 208}, + {0x8017, 208}, + {0xc028, 208}, + {0x8001, 128}, + {0xc016, 128}, + {0x8001, 130}, + {0xc016, 130}, + }, + /* 98 */ + { + {0x8003, 92}, + {0x8006, 92}, + {0x800a, 92}, + {0x800f, 92}, + {0x8018, 92}, + {0x801f, 92}, + {0x8029, 92}, + {0xc038, 92}, + {0x8003, 195}, + {0x8006, 195}, + {0x800a, 195}, + {0x800f, 195}, + {0x8018, 195}, + {0x801f, 195}, + {0x8029, 195}, + {0xc038, 195}, + }, + /* 99 */ + { + {0x8003, 208}, + {0x8006, 208}, + {0x800a, 208}, + {0x800f, 208}, + {0x8018, 208}, + {0x801f, 208}, + {0x8029, 208}, + {0xc038, 208}, + {0x8002, 128}, + {0x8009, 128}, + {0x8017, 128}, + {0xc028, 128}, + {0x8002, 130}, + {0x8009, 130}, + {0x8017, 130}, + {0xc028, 130}, + }, + /* 100 */ + { + {0x8003, 128}, + {0x8006, 128}, + {0x800a, 128}, + {0x800f, 128}, + {0x8018, 128}, + {0x801f, 128}, + {0x8029, 128}, + {0xc038, 128}, + {0x8003, 130}, + {0x8006, 130}, + {0x800a, 130}, + {0x800f, 130}, + {0x8018, 130}, + {0x801f, 130}, + {0x8029, 130}, + {0xc038, 130}, + }, + /* 101 */ + { + {0x8001, 131}, + {0xc016, 131}, + {0x8001, 162}, + {0xc016, 162}, + {0x8001, 184}, + {0xc016, 184}, + {0x8001, 194}, + {0xc016, 194}, + {0x8001, 224}, + {0xc016, 224}, + {0x8001, 226}, + {0xc016, 226}, + {0xc000, 153}, + {0xc000, 161}, + {0xc000, 167}, + {0xc000, 172}, + }, + /* 102 */ + { + {0x8002, 131}, + {0x8009, 131}, + {0x8017, 131}, + {0xc028, 131}, + {0x8002, 162}, + {0x8009, 162}, + {0x8017, 162}, + {0xc028, 162}, + {0x8002, 184}, + {0x8009, 184}, + {0x8017, 184}, + {0xc028, 184}, + {0x8002, 194}, + {0x8009, 194}, + {0x8017, 194}, + {0xc028, 194}, + }, + /* 103 */ + { + {0x8003, 131}, + {0x8006, 131}, + {0x800a, 131}, + {0x800f, 131}, + {0x8018, 131}, + {0x801f, 131}, + {0x8029, 131}, + {0xc038, 131}, + {0x8003, 162}, + {0x8006, 162}, + {0x800a, 162}, + {0x800f, 162}, + {0x8018, 162}, + {0x801f, 162}, + {0x8029, 162}, + {0xc038, 162}, + }, + /* 104 */ + { + {0x8003, 184}, + {0x8006, 184}, + {0x800a, 184}, + {0x800f, 184}, + {0x8018, 184}, + {0x801f, 184}, + {0x8029, 184}, + {0xc038, 184}, + {0x8003, 194}, + {0x8006, 194}, + {0x800a, 194}, + {0x800f, 194}, + {0x8018, 194}, + {0x801f, 194}, + {0x8029, 194}, + {0xc038, 194}, + }, + /* 105 */ + { + {0x8002, 224}, + {0x8009, 224}, + {0x8017, 224}, + {0xc028, 224}, + {0x8002, 226}, + {0x8009, 226}, + {0x8017, 226}, + {0xc028, 226}, + {0x8001, 153}, + {0xc016, 153}, + {0x8001, 161}, + {0xc016, 161}, + {0x8001, 167}, + {0xc016, 167}, + {0x8001, 172}, + {0xc016, 172}, + }, + /* 106 */ + { + {0x8003, 224}, + {0x8006, 224}, + {0x800a, 224}, + {0x800f, 224}, + {0x8018, 224}, + {0x801f, 224}, + {0x8029, 224}, + {0xc038, 224}, + {0x8003, 226}, + {0x8006, 226}, + {0x800a, 226}, + {0x800f, 226}, + {0x8018, 226}, + {0x801f, 226}, + {0x8029, 226}, + {0xc038, 226}, + }, + /* 107 */ + { + {0x8002, 153}, + {0x8009, 153}, + {0x8017, 153}, + {0xc028, 153}, + {0x8002, 161}, + {0x8009, 161}, + {0x8017, 161}, + {0xc028, 161}, + {0x8002, 167}, + {0x8009, 167}, + {0x8017, 167}, + {0xc028, 167}, + {0x8002, 172}, + {0x8009, 172}, + {0x8017, 172}, + {0xc028, 172}, + }, + /* 108 */ + { + {0x8003, 153}, + {0x8006, 153}, + {0x800a, 153}, + {0x800f, 153}, + {0x8018, 153}, + {0x801f, 153}, + {0x8029, 153}, + {0xc038, 153}, + {0x8003, 161}, + {0x8006, 161}, + {0x800a, 161}, + {0x800f, 161}, + {0x8018, 161}, + {0x801f, 161}, + {0x8029, 161}, + {0xc038, 161}, + }, + /* 109 */ + { + {0x8003, 167}, + {0x8006, 167}, + {0x800a, 167}, + {0x800f, 167}, + {0x8018, 167}, + {0x801f, 167}, + {0x8029, 167}, + {0xc038, 167}, + {0x8003, 172}, + {0x8006, 172}, + {0x800a, 172}, + {0x800f, 172}, + {0x8018, 172}, + {0x801f, 172}, + {0x8029, 172}, + {0xc038, 172}, + }, + /* 110 */ + { + {0x72, 0}, + {0x73, 0}, + {0x75, 0}, + {0x76, 0}, + {0x79, 0}, + {0x7b, 0}, + {0x7f, 0}, + {0x82, 0}, + {0x88, 0}, + {0x8b, 0}, + {0x8f, 0}, + {0x92, 0}, + {0x9b, 0}, + {0xa2, 0}, + {0xaa, 0}, + {0xb4, 0}, + }, + /* 111 */ + { + {0xc000, 176}, + {0xc000, 177}, + {0xc000, 179}, + {0xc000, 209}, + {0xc000, 216}, + {0xc000, 217}, + {0xc000, 227}, + {0xc000, 229}, + {0xc000, 230}, + {0x7a, 0}, + {0x7c, 0}, + {0x7d, 0}, + {0x80, 0}, + {0x81, 0}, + {0x83, 0}, + {0x84, 0}, + }, + /* 112 */ + { + {0x8001, 176}, + {0xc016, 176}, + {0x8001, 177}, + {0xc016, 177}, + {0x8001, 179}, + {0xc016, 179}, + {0x8001, 209}, + {0xc016, 209}, + {0x8001, 216}, + {0xc016, 216}, + {0x8001, 217}, + {0xc016, 217}, + {0x8001, 227}, + {0xc016, 227}, + {0x8001, 229}, + {0xc016, 229}, + }, + /* 113 */ + { + {0x8002, 176}, + {0x8009, 176}, + {0x8017, 176}, + {0xc028, 176}, + {0x8002, 177}, + {0x8009, 177}, + {0x8017, 177}, + {0xc028, 177}, + {0x8002, 179}, + {0x8009, 179}, + {0x8017, 179}, + {0xc028, 179}, + {0x8002, 209}, + {0x8009, 209}, + {0x8017, 209}, + {0xc028, 209}, + }, + /* 114 */ + { + {0x8003, 176}, + {0x8006, 176}, + {0x800a, 176}, + {0x800f, 176}, + {0x8018, 176}, + {0x801f, 176}, + {0x8029, 176}, + {0xc038, 176}, + {0x8003, 177}, + {0x8006, 177}, + {0x800a, 177}, + {0x800f, 177}, + {0x8018, 177}, + {0x801f, 177}, + {0x8029, 177}, + {0xc038, 177}, + }, + /* 115 */ + { + {0x8003, 179}, + {0x8006, 179}, + {0x800a, 179}, + {0x800f, 179}, + {0x8018, 179}, + {0x801f, 179}, + {0x8029, 179}, + {0xc038, 179}, + {0x8003, 209}, + {0x8006, 209}, + {0x800a, 209}, + {0x800f, 209}, + {0x8018, 209}, + {0x801f, 209}, + {0x8029, 209}, + {0xc038, 209}, + }, + /* 116 */ + { + {0x8002, 216}, + {0x8009, 216}, + {0x8017, 216}, + {0xc028, 216}, + {0x8002, 217}, + {0x8009, 217}, + {0x8017, 217}, + {0xc028, 217}, + {0x8002, 227}, + {0x8009, 227}, + {0x8017, 227}, + {0xc028, 227}, + {0x8002, 229}, + {0x8009, 229}, + {0x8017, 229}, + {0xc028, 229}, + }, + /* 117 */ + { + {0x8003, 216}, + {0x8006, 216}, + {0x800a, 216}, + {0x800f, 216}, + {0x8018, 216}, + {0x801f, 216}, + {0x8029, 216}, + {0xc038, 216}, + {0x8003, 217}, + {0x8006, 217}, + {0x800a, 217}, + {0x800f, 217}, + {0x8018, 217}, + {0x801f, 217}, + {0x8029, 217}, + {0xc038, 217}, + }, + /* 118 */ + { + {0x8003, 227}, + {0x8006, 227}, + {0x800a, 227}, + {0x800f, 227}, + {0x8018, 227}, + {0x801f, 227}, + {0x8029, 227}, + {0xc038, 227}, + {0x8003, 229}, + {0x8006, 229}, + {0x800a, 229}, + {0x800f, 229}, + {0x8018, 229}, + {0x801f, 229}, + {0x8029, 229}, + {0xc038, 229}, + }, + /* 119 */ + { + {0x8001, 230}, + {0xc016, 230}, + {0xc000, 129}, + {0xc000, 132}, + {0xc000, 133}, + {0xc000, 134}, + {0xc000, 136}, + {0xc000, 146}, + {0xc000, 154}, + {0xc000, 156}, + {0xc000, 160}, + {0xc000, 163}, + {0xc000, 164}, + {0xc000, 169}, + {0xc000, 170}, + {0xc000, 173}, + }, + /* 120 */ + { + {0x8002, 230}, + {0x8009, 230}, + {0x8017, 230}, + {0xc028, 230}, + {0x8001, 129}, + {0xc016, 129}, + {0x8001, 132}, + {0xc016, 132}, + {0x8001, 133}, + {0xc016, 133}, + {0x8001, 134}, + {0xc016, 134}, + {0x8001, 136}, + {0xc016, 136}, + {0x8001, 146}, + {0xc016, 146}, + }, + /* 121 */ + { + {0x8003, 230}, + {0x8006, 230}, + {0x800a, 230}, + {0x800f, 230}, + {0x8018, 230}, + {0x801f, 230}, + {0x8029, 230}, + {0xc038, 230}, + {0x8002, 129}, + {0x8009, 129}, + {0x8017, 129}, + {0xc028, 129}, + {0x8002, 132}, + {0x8009, 132}, + {0x8017, 132}, + {0xc028, 132}, + }, + /* 122 */ + { + {0x8003, 129}, + {0x8006, 129}, + {0x800a, 129}, + {0x800f, 129}, + {0x8018, 129}, + {0x801f, 129}, + {0x8029, 129}, + {0xc038, 129}, + {0x8003, 132}, + {0x8006, 132}, + {0x800a, 132}, + {0x800f, 132}, + {0x8018, 132}, + {0x801f, 132}, + {0x8029, 132}, + {0xc038, 132}, + }, + /* 123 */ + { + {0x8002, 133}, + {0x8009, 133}, + {0x8017, 133}, + {0xc028, 133}, + {0x8002, 134}, + {0x8009, 134}, + {0x8017, 134}, + {0xc028, 134}, + {0x8002, 136}, + {0x8009, 136}, + {0x8017, 136}, + {0xc028, 136}, + {0x8002, 146}, + {0x8009, 146}, + {0x8017, 146}, + {0xc028, 146}, + }, + /* 124 */ + { + {0x8003, 133}, + {0x8006, 133}, + {0x800a, 133}, + {0x800f, 133}, + {0x8018, 133}, + {0x801f, 133}, + {0x8029, 133}, + {0xc038, 133}, + {0x8003, 134}, + {0x8006, 134}, + {0x800a, 134}, + {0x800f, 134}, + {0x8018, 134}, + {0x801f, 134}, + {0x8029, 134}, + {0xc038, 134}, + }, + /* 125 */ + { + {0x8003, 136}, + {0x8006, 136}, + {0x800a, 136}, + {0x800f, 136}, + {0x8018, 136}, + {0x801f, 136}, + {0x8029, 136}, + {0xc038, 136}, + {0x8003, 146}, + {0x8006, 146}, + {0x800a, 146}, + {0x800f, 146}, + {0x8018, 146}, + {0x801f, 146}, + {0x8029, 146}, + {0xc038, 146}, + }, + /* 126 */ + { + {0x8001, 154}, + {0xc016, 154}, + {0x8001, 156}, + {0xc016, 156}, + {0x8001, 160}, + {0xc016, 160}, + {0x8001, 163}, + {0xc016, 163}, + {0x8001, 164}, + {0xc016, 164}, + {0x8001, 169}, + {0xc016, 169}, + {0x8001, 170}, + {0xc016, 170}, + {0x8001, 173}, + {0xc016, 173}, + }, + /* 127 */ + { + {0x8002, 154}, + {0x8009, 154}, + {0x8017, 154}, + {0xc028, 154}, + {0x8002, 156}, + {0x8009, 156}, + {0x8017, 156}, + {0xc028, 156}, + {0x8002, 160}, + {0x8009, 160}, + {0x8017, 160}, + {0xc028, 160}, + {0x8002, 163}, + {0x8009, 163}, + {0x8017, 163}, + {0xc028, 163}, + }, + /* 128 */ + { + {0x8003, 154}, + {0x8006, 154}, + {0x800a, 154}, + {0x800f, 154}, + {0x8018, 154}, + {0x801f, 154}, + {0x8029, 154}, + {0xc038, 154}, + {0x8003, 156}, + {0x8006, 156}, + {0x800a, 156}, + {0x800f, 156}, + {0x8018, 156}, + {0x801f, 156}, + {0x8029, 156}, + {0xc038, 156}, + }, + /* 129 */ + { + {0x8003, 160}, + {0x8006, 160}, + {0x800a, 160}, + {0x800f, 160}, + {0x8018, 160}, + {0x801f, 160}, + {0x8029, 160}, + {0xc038, 160}, + {0x8003, 163}, + {0x8006, 163}, + {0x800a, 163}, + {0x800f, 163}, + {0x8018, 163}, + {0x801f, 163}, + {0x8029, 163}, + {0xc038, 163}, + }, + /* 130 */ + { + {0x8002, 164}, + {0x8009, 164}, + {0x8017, 164}, + {0xc028, 164}, + {0x8002, 169}, + {0x8009, 169}, + {0x8017, 169}, + {0xc028, 169}, + {0x8002, 170}, + {0x8009, 170}, + {0x8017, 170}, + {0xc028, 170}, + {0x8002, 173}, + {0x8009, 173}, + {0x8017, 173}, + {0xc028, 173}, + }, + /* 131 */ + { + {0x8003, 164}, + {0x8006, 164}, + {0x800a, 164}, + {0x800f, 164}, + {0x8018, 164}, + {0x801f, 164}, + {0x8029, 164}, + {0xc038, 164}, + {0x8003, 169}, + {0x8006, 169}, + {0x800a, 169}, + {0x800f, 169}, + {0x8018, 169}, + {0x801f, 169}, + {0x8029, 169}, + {0xc038, 169}, + }, + /* 132 */ + { + {0x8003, 170}, + {0x8006, 170}, + {0x800a, 170}, + {0x800f, 170}, + {0x8018, 170}, + {0x801f, 170}, + {0x8029, 170}, + {0xc038, 170}, + {0x8003, 173}, + {0x8006, 173}, + {0x800a, 173}, + {0x800f, 173}, + {0x8018, 173}, + {0x801f, 173}, + {0x8029, 173}, + {0xc038, 173}, + }, + /* 133 */ + { + {0x89, 0}, + {0x8a, 0}, + {0x8c, 0}, + {0x8d, 0}, + {0x90, 0}, + {0x91, 0}, + {0x93, 0}, + {0x96, 0}, + {0x9c, 0}, + {0x9f, 0}, + {0xa3, 0}, + {0xa6, 0}, + {0xab, 0}, + {0xae, 0}, + {0xb5, 0}, + {0xbe, 0}, + }, + /* 134 */ + { + {0xc000, 178}, + {0xc000, 181}, + {0xc000, 185}, + {0xc000, 186}, + {0xc000, 187}, + {0xc000, 189}, + {0xc000, 190}, + {0xc000, 196}, + {0xc000, 198}, + {0xc000, 228}, + {0xc000, 232}, + {0xc000, 233}, + {0x94, 0}, + {0x95, 0}, + {0x97, 0}, + {0x98, 0}, + }, + /* 135 */ + { + {0x8001, 178}, + {0xc016, 178}, + {0x8001, 181}, + {0xc016, 181}, + {0x8001, 185}, + {0xc016, 185}, + {0x8001, 186}, + {0xc016, 186}, + {0x8001, 187}, + {0xc016, 187}, + {0x8001, 189}, + {0xc016, 189}, + {0x8001, 190}, + {0xc016, 190}, + {0x8001, 196}, + {0xc016, 196}, + }, + /* 136 */ + { + {0x8002, 178}, + {0x8009, 178}, + {0x8017, 178}, + {0xc028, 178}, + {0x8002, 181}, + {0x8009, 181}, + {0x8017, 181}, + {0xc028, 181}, + {0x8002, 185}, + {0x8009, 185}, + {0x8017, 185}, + {0xc028, 185}, + {0x8002, 186}, + {0x8009, 186}, + {0x8017, 186}, + {0xc028, 186}, + }, + /* 137 */ + { + {0x8003, 178}, + {0x8006, 178}, + {0x800a, 178}, + {0x800f, 178}, + {0x8018, 178}, + {0x801f, 178}, + {0x8029, 178}, + {0xc038, 178}, + {0x8003, 181}, + {0x8006, 181}, + {0x800a, 181}, + {0x800f, 181}, + {0x8018, 181}, + {0x801f, 181}, + {0x8029, 181}, + {0xc038, 181}, + }, + /* 138 */ + { + {0x8003, 185}, + {0x8006, 185}, + {0x800a, 185}, + {0x800f, 185}, + {0x8018, 185}, + {0x801f, 185}, + {0x8029, 185}, + {0xc038, 185}, + {0x8003, 186}, + {0x8006, 186}, + {0x800a, 186}, + {0x800f, 186}, + {0x8018, 186}, + {0x801f, 186}, + {0x8029, 186}, + {0xc038, 186}, + }, + /* 139 */ + { + {0x8002, 187}, + {0x8009, 187}, + {0x8017, 187}, + {0xc028, 187}, + {0x8002, 189}, + {0x8009, 189}, + {0x8017, 189}, + {0xc028, 189}, + {0x8002, 190}, + {0x8009, 190}, + {0x8017, 190}, + {0xc028, 190}, + {0x8002, 196}, + {0x8009, 196}, + {0x8017, 196}, + {0xc028, 196}, + }, + /* 140 */ + { + {0x8003, 187}, + {0x8006, 187}, + {0x800a, 187}, + {0x800f, 187}, + {0x8018, 187}, + {0x801f, 187}, + {0x8029, 187}, + {0xc038, 187}, + {0x8003, 189}, + {0x8006, 189}, + {0x800a, 189}, + {0x800f, 189}, + {0x8018, 189}, + {0x801f, 189}, + {0x8029, 189}, + {0xc038, 189}, + }, + /* 141 */ + { + {0x8003, 190}, + {0x8006, 190}, + {0x800a, 190}, + {0x800f, 190}, + {0x8018, 190}, + {0x801f, 190}, + {0x8029, 190}, + {0xc038, 190}, + {0x8003, 196}, + {0x8006, 196}, + {0x800a, 196}, + {0x800f, 196}, + {0x8018, 196}, + {0x801f, 196}, + {0x8029, 196}, + {0xc038, 196}, + }, + /* 142 */ + { + {0x8001, 198}, + {0xc016, 198}, + {0x8001, 228}, + {0xc016, 228}, + {0x8001, 232}, + {0xc016, 232}, + {0x8001, 233}, + {0xc016, 233}, + {0xc000, 1}, + {0xc000, 135}, + {0xc000, 137}, + {0xc000, 138}, + {0xc000, 139}, + {0xc000, 140}, + {0xc000, 141}, + {0xc000, 143}, + }, + /* 143 */ + { + {0x8002, 198}, + {0x8009, 198}, + {0x8017, 198}, + {0xc028, 198}, + {0x8002, 228}, + {0x8009, 228}, + {0x8017, 228}, + {0xc028, 228}, + {0x8002, 232}, + {0x8009, 232}, + {0x8017, 232}, + {0xc028, 232}, + {0x8002, 233}, + {0x8009, 233}, + {0x8017, 233}, + {0xc028, 233}, + }, + /* 144 */ + { + {0x8003, 198}, + {0x8006, 198}, + {0x800a, 198}, + {0x800f, 198}, + {0x8018, 198}, + {0x801f, 198}, + {0x8029, 198}, + {0xc038, 198}, + {0x8003, 228}, + {0x8006, 228}, + {0x800a, 228}, + {0x800f, 228}, + {0x8018, 228}, + {0x801f, 228}, + {0x8029, 228}, + {0xc038, 228}, + }, + /* 145 */ + { + {0x8003, 232}, + {0x8006, 232}, + {0x800a, 232}, + {0x800f, 232}, + {0x8018, 232}, + {0x801f, 232}, + {0x8029, 232}, + {0xc038, 232}, + {0x8003, 233}, + {0x8006, 233}, + {0x800a, 233}, + {0x800f, 233}, + {0x8018, 233}, + {0x801f, 233}, + {0x8029, 233}, + {0xc038, 233}, + }, + /* 146 */ + { + {0x8001, 1}, + {0xc016, 1}, + {0x8001, 135}, + {0xc016, 135}, + {0x8001, 137}, + {0xc016, 137}, + {0x8001, 138}, + {0xc016, 138}, + {0x8001, 139}, + {0xc016, 139}, + {0x8001, 140}, + {0xc016, 140}, + {0x8001, 141}, + {0xc016, 141}, + {0x8001, 143}, + {0xc016, 143}, + }, + /* 147 */ + { + {0x8002, 1}, + {0x8009, 1}, + {0x8017, 1}, + {0xc028, 1}, + {0x8002, 135}, + {0x8009, 135}, + {0x8017, 135}, + {0xc028, 135}, + {0x8002, 137}, + {0x8009, 137}, + {0x8017, 137}, + {0xc028, 137}, + {0x8002, 138}, + {0x8009, 138}, + {0x8017, 138}, + {0xc028, 138}, + }, + /* 148 */ + { + {0x8003, 1}, + {0x8006, 1}, + {0x800a, 1}, + {0x800f, 1}, + {0x8018, 1}, + {0x801f, 1}, + {0x8029, 1}, + {0xc038, 1}, + {0x8003, 135}, + {0x8006, 135}, + {0x800a, 135}, + {0x800f, 135}, + {0x8018, 135}, + {0x801f, 135}, + {0x8029, 135}, + {0xc038, 135}, + }, + /* 149 */ + { + {0x8003, 137}, + {0x8006, 137}, + {0x800a, 137}, + {0x800f, 137}, + {0x8018, 137}, + {0x801f, 137}, + {0x8029, 137}, + {0xc038, 137}, + {0x8003, 138}, + {0x8006, 138}, + {0x800a, 138}, + {0x800f, 138}, + {0x8018, 138}, + {0x801f, 138}, + {0x8029, 138}, + {0xc038, 138}, + }, + /* 150 */ + { + {0x8002, 139}, + {0x8009, 139}, + {0x8017, 139}, + {0xc028, 139}, + {0x8002, 140}, + {0x8009, 140}, + {0x8017, 140}, + {0xc028, 140}, + {0x8002, 141}, + {0x8009, 141}, + {0x8017, 141}, + {0xc028, 141}, + {0x8002, 143}, + {0x8009, 143}, + {0x8017, 143}, + {0xc028, 143}, + }, + /* 151 */ + { + {0x8003, 139}, + {0x8006, 139}, + {0x800a, 139}, + {0x800f, 139}, + {0x8018, 139}, + {0x801f, 139}, + {0x8029, 139}, + {0xc038, 139}, + {0x8003, 140}, + {0x8006, 140}, + {0x800a, 140}, + {0x800f, 140}, + {0x8018, 140}, + {0x801f, 140}, + {0x8029, 140}, + {0xc038, 140}, + }, + /* 152 */ + { + {0x8003, 141}, + {0x8006, 141}, + {0x800a, 141}, + {0x800f, 141}, + {0x8018, 141}, + {0x801f, 141}, + {0x8029, 141}, + {0xc038, 141}, + {0x8003, 143}, + {0x8006, 143}, + {0x800a, 143}, + {0x800f, 143}, + {0x8018, 143}, + {0x801f, 143}, + {0x8029, 143}, + {0xc038, 143}, + }, + /* 153 */ + { + {0x9d, 0}, + {0x9e, 0}, + {0xa0, 0}, + {0xa1, 0}, + {0xa4, 0}, + {0xa5, 0}, + {0xa7, 0}, + {0xa8, 0}, + {0xac, 0}, + {0xad, 0}, + {0xaf, 0}, + {0xb1, 0}, + {0xb6, 0}, + {0xb9, 0}, + {0xbf, 0}, + {0xcf, 0}, + }, + /* 154 */ + { + {0xc000, 147}, + {0xc000, 149}, + {0xc000, 150}, + {0xc000, 151}, + {0xc000, 152}, + {0xc000, 155}, + {0xc000, 157}, + {0xc000, 158}, + {0xc000, 165}, + {0xc000, 166}, + {0xc000, 168}, + {0xc000, 174}, + {0xc000, 175}, + {0xc000, 180}, + {0xc000, 182}, + {0xc000, 183}, + }, + /* 155 */ + { + {0x8001, 147}, + {0xc016, 147}, + {0x8001, 149}, + {0xc016, 149}, + {0x8001, 150}, + {0xc016, 150}, + {0x8001, 151}, + {0xc016, 151}, + {0x8001, 152}, + {0xc016, 152}, + {0x8001, 155}, + {0xc016, 155}, + {0x8001, 157}, + {0xc016, 157}, + {0x8001, 158}, + {0xc016, 158}, + }, + /* 156 */ + { + {0x8002, 147}, + {0x8009, 147}, + {0x8017, 147}, + {0xc028, 147}, + {0x8002, 149}, + {0x8009, 149}, + {0x8017, 149}, + {0xc028, 149}, + {0x8002, 150}, + {0x8009, 150}, + {0x8017, 150}, + {0xc028, 150}, + {0x8002, 151}, + {0x8009, 151}, + {0x8017, 151}, + {0xc028, 151}, + }, + /* 157 */ + { + {0x8003, 147}, + {0x8006, 147}, + {0x800a, 147}, + {0x800f, 147}, + {0x8018, 147}, + {0x801f, 147}, + {0x8029, 147}, + {0xc038, 147}, + {0x8003, 149}, + {0x8006, 149}, + {0x800a, 149}, + {0x800f, 149}, + {0x8018, 149}, + {0x801f, 149}, + {0x8029, 149}, + {0xc038, 149}, + }, + /* 158 */ + { + {0x8003, 150}, + {0x8006, 150}, + {0x800a, 150}, + {0x800f, 150}, + {0x8018, 150}, + {0x801f, 150}, + {0x8029, 150}, + {0xc038, 150}, + {0x8003, 151}, + {0x8006, 151}, + {0x800a, 151}, + {0x800f, 151}, + {0x8018, 151}, + {0x801f, 151}, + {0x8029, 151}, + {0xc038, 151}, + }, + /* 159 */ + { + {0x8002, 152}, + {0x8009, 152}, + {0x8017, 152}, + {0xc028, 152}, + {0x8002, 155}, + {0x8009, 155}, + {0x8017, 155}, + {0xc028, 155}, + {0x8002, 157}, + {0x8009, 157}, + {0x8017, 157}, + {0xc028, 157}, + {0x8002, 158}, + {0x8009, 158}, + {0x8017, 158}, + {0xc028, 158}, + }, + /* 160 */ + { + {0x8003, 152}, + {0x8006, 152}, + {0x800a, 152}, + {0x800f, 152}, + {0x8018, 152}, + {0x801f, 152}, + {0x8029, 152}, + {0xc038, 152}, + {0x8003, 155}, + {0x8006, 155}, + {0x800a, 155}, + {0x800f, 155}, + {0x8018, 155}, + {0x801f, 155}, + {0x8029, 155}, + {0xc038, 155}, + }, + /* 161 */ + { + {0x8003, 157}, + {0x8006, 157}, + {0x800a, 157}, + {0x800f, 157}, + {0x8018, 157}, + {0x801f, 157}, + {0x8029, 157}, + {0xc038, 157}, + {0x8003, 158}, + {0x8006, 158}, + {0x800a, 158}, + {0x800f, 158}, + {0x8018, 158}, + {0x801f, 158}, + {0x8029, 158}, + {0xc038, 158}, + }, + /* 162 */ + { + {0x8001, 165}, + {0xc016, 165}, + {0x8001, 166}, + {0xc016, 166}, + {0x8001, 168}, + {0xc016, 168}, + {0x8001, 174}, + {0xc016, 174}, + {0x8001, 175}, + {0xc016, 175}, + {0x8001, 180}, + {0xc016, 180}, + {0x8001, 182}, + {0xc016, 182}, + {0x8001, 183}, + {0xc016, 183}, + }, + /* 163 */ + { + {0x8002, 165}, + {0x8009, 165}, + {0x8017, 165}, + {0xc028, 165}, + {0x8002, 166}, + {0x8009, 166}, + {0x8017, 166}, + {0xc028, 166}, + {0x8002, 168}, + {0x8009, 168}, + {0x8017, 168}, + {0xc028, 168}, + {0x8002, 174}, + {0x8009, 174}, + {0x8017, 174}, + {0xc028, 174}, + }, + /* 164 */ + { + {0x8003, 165}, + {0x8006, 165}, + {0x800a, 165}, + {0x800f, 165}, + {0x8018, 165}, + {0x801f, 165}, + {0x8029, 165}, + {0xc038, 165}, + {0x8003, 166}, + {0x8006, 166}, + {0x800a, 166}, + {0x800f, 166}, + {0x8018, 166}, + {0x801f, 166}, + {0x8029, 166}, + {0xc038, 166}, + }, + /* 165 */ + { + {0x8003, 168}, + {0x8006, 168}, + {0x800a, 168}, + {0x800f, 168}, + {0x8018, 168}, + {0x801f, 168}, + {0x8029, 168}, + {0xc038, 168}, + {0x8003, 174}, + {0x8006, 174}, + {0x800a, 174}, + {0x800f, 174}, + {0x8018, 174}, + {0x801f, 174}, + {0x8029, 174}, + {0xc038, 174}, + }, + /* 166 */ + { + {0x8002, 175}, + {0x8009, 175}, + {0x8017, 175}, + {0xc028, 175}, + {0x8002, 180}, + {0x8009, 180}, + {0x8017, 180}, + {0xc028, 180}, + {0x8002, 182}, + {0x8009, 182}, + {0x8017, 182}, + {0xc028, 182}, + {0x8002, 183}, + {0x8009, 183}, + {0x8017, 183}, + {0xc028, 183}, + }, + /* 167 */ + { + {0x8003, 175}, + {0x8006, 175}, + {0x800a, 175}, + {0x800f, 175}, + {0x8018, 175}, + {0x801f, 175}, + {0x8029, 175}, + {0xc038, 175}, + {0x8003, 180}, + {0x8006, 180}, + {0x800a, 180}, + {0x800f, 180}, + {0x8018, 180}, + {0x801f, 180}, + {0x8029, 180}, + {0xc038, 180}, + }, + /* 168 */ + { + {0x8003, 182}, + {0x8006, 182}, + {0x800a, 182}, + {0x800f, 182}, + {0x8018, 182}, + {0x801f, 182}, + {0x8029, 182}, + {0xc038, 182}, + {0x8003, 183}, + {0x8006, 183}, + {0x800a, 183}, + {0x800f, 183}, + {0x8018, 183}, + {0x801f, 183}, + {0x8029, 183}, + {0xc038, 183}, + }, + /* 169 */ + { + {0xc000, 188}, + {0xc000, 191}, + {0xc000, 197}, + {0xc000, 231}, + {0xc000, 239}, + {0xb0, 0}, + {0xb2, 0}, + {0xb3, 0}, + {0xb7, 0}, + {0xb8, 0}, + {0xba, 0}, + {0xbb, 0}, + {0xc0, 0}, + {0xc7, 0}, + {0xd0, 0}, + {0xdf, 0}, + }, + /* 170 */ + { + {0x8001, 188}, + {0xc016, 188}, + {0x8001, 191}, + {0xc016, 191}, + {0x8001, 197}, + {0xc016, 197}, + {0x8001, 231}, + {0xc016, 231}, + {0x8001, 239}, + {0xc016, 239}, + {0xc000, 9}, + {0xc000, 142}, + {0xc000, 144}, + {0xc000, 145}, + {0xc000, 148}, + {0xc000, 159}, + }, + /* 171 */ + { + {0x8002, 188}, + {0x8009, 188}, + {0x8017, 188}, + {0xc028, 188}, + {0x8002, 191}, + {0x8009, 191}, + {0x8017, 191}, + {0xc028, 191}, + {0x8002, 197}, + {0x8009, 197}, + {0x8017, 197}, + {0xc028, 197}, + {0x8002, 231}, + {0x8009, 231}, + {0x8017, 231}, + {0xc028, 231}, + }, + /* 172 */ + { + {0x8003, 188}, + {0x8006, 188}, + {0x800a, 188}, + {0x800f, 188}, + {0x8018, 188}, + {0x801f, 188}, + {0x8029, 188}, + {0xc038, 188}, + {0x8003, 191}, + {0x8006, 191}, + {0x800a, 191}, + {0x800f, 191}, + {0x8018, 191}, + {0x801f, 191}, + {0x8029, 191}, + {0xc038, 191}, + }, + /* 173 */ + { + {0x8003, 197}, + {0x8006, 197}, + {0x800a, 197}, + {0x800f, 197}, + {0x8018, 197}, + {0x801f, 197}, + {0x8029, 197}, + {0xc038, 197}, + {0x8003, 231}, + {0x8006, 231}, + {0x800a, 231}, + {0x800f, 231}, + {0x8018, 231}, + {0x801f, 231}, + {0x8029, 231}, + {0xc038, 231}, + }, + /* 174 */ + { + {0x8002, 239}, + {0x8009, 239}, + {0x8017, 239}, + {0xc028, 239}, + {0x8001, 9}, + {0xc016, 9}, + {0x8001, 142}, + {0xc016, 142}, + {0x8001, 144}, + {0xc016, 144}, + {0x8001, 145}, + {0xc016, 145}, + {0x8001, 148}, + {0xc016, 148}, + {0x8001, 159}, + {0xc016, 159}, + }, + /* 175 */ + { + {0x8003, 239}, + {0x8006, 239}, + {0x800a, 239}, + {0x800f, 239}, + {0x8018, 239}, + {0x801f, 239}, + {0x8029, 239}, + {0xc038, 239}, + {0x8002, 9}, + {0x8009, 9}, + {0x8017, 9}, + {0xc028, 9}, + {0x8002, 142}, + {0x8009, 142}, + {0x8017, 142}, + {0xc028, 142}, + }, + /* 176 */ + { + {0x8003, 9}, + {0x8006, 9}, + {0x800a, 9}, + {0x800f, 9}, + {0x8018, 9}, + {0x801f, 9}, + {0x8029, 9}, + {0xc038, 9}, + {0x8003, 142}, + {0x8006, 142}, + {0x800a, 142}, + {0x800f, 142}, + {0x8018, 142}, + {0x801f, 142}, + {0x8029, 142}, + {0xc038, 142}, + }, + /* 177 */ + { + {0x8002, 144}, + {0x8009, 144}, + {0x8017, 144}, + {0xc028, 144}, + {0x8002, 145}, + {0x8009, 145}, + {0x8017, 145}, + {0xc028, 145}, + {0x8002, 148}, + {0x8009, 148}, + {0x8017, 148}, + {0xc028, 148}, + {0x8002, 159}, + {0x8009, 159}, + {0x8017, 159}, + {0xc028, 159}, + }, + /* 178 */ + { + {0x8003, 144}, + {0x8006, 144}, + {0x800a, 144}, + {0x800f, 144}, + {0x8018, 144}, + {0x801f, 144}, + {0x8029, 144}, + {0xc038, 144}, + {0x8003, 145}, + {0x8006, 145}, + {0x800a, 145}, + {0x800f, 145}, + {0x8018, 145}, + {0x801f, 145}, + {0x8029, 145}, + {0xc038, 145}, + }, + /* 179 */ + { + {0x8003, 148}, + {0x8006, 148}, + {0x800a, 148}, + {0x800f, 148}, + {0x8018, 148}, + {0x801f, 148}, + {0x8029, 148}, + {0xc038, 148}, + {0x8003, 159}, + {0x8006, 159}, + {0x800a, 159}, + {0x800f, 159}, + {0x8018, 159}, + {0x801f, 159}, + {0x8029, 159}, + {0xc038, 159}, + }, + /* 180 */ + { + {0xc000, 171}, + {0xc000, 206}, + {0xc000, 215}, + {0xc000, 225}, + {0xc000, 236}, + {0xc000, 237}, + {0xbc, 0}, + {0xbd, 0}, + {0xc1, 0}, + {0xc4, 0}, + {0xc8, 0}, + {0xcb, 0}, + {0xd1, 0}, + {0xd8, 0}, + {0xe0, 0}, + {0xee, 0}, + }, + /* 181 */ + { + {0x8001, 171}, + {0xc016, 171}, + {0x8001, 206}, + {0xc016, 206}, + {0x8001, 215}, + {0xc016, 215}, + {0x8001, 225}, + {0xc016, 225}, + {0x8001, 236}, + {0xc016, 236}, + {0x8001, 237}, + {0xc016, 237}, + {0xc000, 199}, + {0xc000, 207}, + {0xc000, 234}, + {0xc000, 235}, + }, + /* 182 */ + { + {0x8002, 171}, + {0x8009, 171}, + {0x8017, 171}, + {0xc028, 171}, + {0x8002, 206}, + {0x8009, 206}, + {0x8017, 206}, + {0xc028, 206}, + {0x8002, 215}, + {0x8009, 215}, + {0x8017, 215}, + {0xc028, 215}, + {0x8002, 225}, + {0x8009, 225}, + {0x8017, 225}, + {0xc028, 225}, + }, + /* 183 */ + { + {0x8003, 171}, + {0x8006, 171}, + {0x800a, 171}, + {0x800f, 171}, + {0x8018, 171}, + {0x801f, 171}, + {0x8029, 171}, + {0xc038, 171}, + {0x8003, 206}, + {0x8006, 206}, + {0x800a, 206}, + {0x800f, 206}, + {0x8018, 206}, + {0x801f, 206}, + {0x8029, 206}, + {0xc038, 206}, + }, + /* 184 */ + { + {0x8003, 215}, + {0x8006, 215}, + {0x800a, 215}, + {0x800f, 215}, + {0x8018, 215}, + {0x801f, 215}, + {0x8029, 215}, + {0xc038, 215}, + {0x8003, 225}, + {0x8006, 225}, + {0x800a, 225}, + {0x800f, 225}, + {0x8018, 225}, + {0x801f, 225}, + {0x8029, 225}, + {0xc038, 225}, + }, + /* 185 */ + { + {0x8002, 236}, + {0x8009, 236}, + {0x8017, 236}, + {0xc028, 236}, + {0x8002, 237}, + {0x8009, 237}, + {0x8017, 237}, + {0xc028, 237}, + {0x8001, 199}, + {0xc016, 199}, + {0x8001, 207}, + {0xc016, 207}, + {0x8001, 234}, + {0xc016, 234}, + {0x8001, 235}, + {0xc016, 235}, + }, + /* 186 */ + { + {0x8003, 236}, + {0x8006, 236}, + {0x800a, 236}, + {0x800f, 236}, + {0x8018, 236}, + {0x801f, 236}, + {0x8029, 236}, + {0xc038, 236}, + {0x8003, 237}, + {0x8006, 237}, + {0x800a, 237}, + {0x800f, 237}, + {0x8018, 237}, + {0x801f, 237}, + {0x8029, 237}, + {0xc038, 237}, + }, + /* 187 */ + { + {0x8002, 199}, + {0x8009, 199}, + {0x8017, 199}, + {0xc028, 199}, + {0x8002, 207}, + {0x8009, 207}, + {0x8017, 207}, + {0xc028, 207}, + {0x8002, 234}, + {0x8009, 234}, + {0x8017, 234}, + {0xc028, 234}, + {0x8002, 235}, + {0x8009, 235}, + {0x8017, 235}, + {0xc028, 235}, + }, + /* 188 */ + { + {0x8003, 199}, + {0x8006, 199}, + {0x800a, 199}, + {0x800f, 199}, + {0x8018, 199}, + {0x801f, 199}, + {0x8029, 199}, + {0xc038, 199}, + {0x8003, 207}, + {0x8006, 207}, + {0x800a, 207}, + {0x800f, 207}, + {0x8018, 207}, + {0x801f, 207}, + {0x8029, 207}, + {0xc038, 207}, + }, + /* 189 */ + { + {0x8003, 234}, + {0x8006, 234}, + {0x800a, 234}, + {0x800f, 234}, + {0x8018, 234}, + {0x801f, 234}, + {0x8029, 234}, + {0xc038, 234}, + {0x8003, 235}, + {0x8006, 235}, + {0x800a, 235}, + {0x800f, 235}, + {0x8018, 235}, + {0x801f, 235}, + {0x8029, 235}, + {0xc038, 235}, + }, + /* 190 */ + { + {0xc2, 0}, + {0xc3, 0}, + {0xc5, 0}, + {0xc6, 0}, + {0xc9, 0}, + {0xca, 0}, + {0xcc, 0}, + {0xcd, 0}, + {0xd2, 0}, + {0xd5, 0}, + {0xd9, 0}, + {0xdc, 0}, + {0xe1, 0}, + {0xe7, 0}, + {0xef, 0}, + {0xf6, 0}, + }, + /* 191 */ + { + {0xc000, 192}, + {0xc000, 193}, + {0xc000, 200}, + {0xc000, 201}, + {0xc000, 202}, + {0xc000, 205}, + {0xc000, 210}, + {0xc000, 213}, + {0xc000, 218}, + {0xc000, 219}, + {0xc000, 238}, + {0xc000, 240}, + {0xc000, 242}, + {0xc000, 243}, + {0xc000, 255}, + {0xce, 0}, + }, + /* 192 */ + { + {0x8001, 192}, + {0xc016, 192}, + {0x8001, 193}, + {0xc016, 193}, + {0x8001, 200}, + {0xc016, 200}, + {0x8001, 201}, + {0xc016, 201}, + {0x8001, 202}, + {0xc016, 202}, + {0x8001, 205}, + {0xc016, 205}, + {0x8001, 210}, + {0xc016, 210}, + {0x8001, 213}, + {0xc016, 213}, + }, + /* 193 */ + { + {0x8002, 192}, + {0x8009, 192}, + {0x8017, 192}, + {0xc028, 192}, + {0x8002, 193}, + {0x8009, 193}, + {0x8017, 193}, + {0xc028, 193}, + {0x8002, 200}, + {0x8009, 200}, + {0x8017, 200}, + {0xc028, 200}, + {0x8002, 201}, + {0x8009, 201}, + {0x8017, 201}, + {0xc028, 201}, + }, + /* 194 */ + { + {0x8003, 192}, + {0x8006, 192}, + {0x800a, 192}, + {0x800f, 192}, + {0x8018, 192}, + {0x801f, 192}, + {0x8029, 192}, + {0xc038, 192}, + {0x8003, 193}, + {0x8006, 193}, + {0x800a, 193}, + {0x800f, 193}, + {0x8018, 193}, + {0x801f, 193}, + {0x8029, 193}, + {0xc038, 193}, + }, + /* 195 */ + { + {0x8003, 200}, + {0x8006, 200}, + {0x800a, 200}, + {0x800f, 200}, + {0x8018, 200}, + {0x801f, 200}, + {0x8029, 200}, + {0xc038, 200}, + {0x8003, 201}, + {0x8006, 201}, + {0x800a, 201}, + {0x800f, 201}, + {0x8018, 201}, + {0x801f, 201}, + {0x8029, 201}, + {0xc038, 201}, + }, + /* 196 */ + { + {0x8002, 202}, + {0x8009, 202}, + {0x8017, 202}, + {0xc028, 202}, + {0x8002, 205}, + {0x8009, 205}, + {0x8017, 205}, + {0xc028, 205}, + {0x8002, 210}, + {0x8009, 210}, + {0x8017, 210}, + {0xc028, 210}, + {0x8002, 213}, + {0x8009, 213}, + {0x8017, 213}, + {0xc028, 213}, + }, + /* 197 */ + { + {0x8003, 202}, + {0x8006, 202}, + {0x800a, 202}, + {0x800f, 202}, + {0x8018, 202}, + {0x801f, 202}, + {0x8029, 202}, + {0xc038, 202}, + {0x8003, 205}, + {0x8006, 205}, + {0x800a, 205}, + {0x800f, 205}, + {0x8018, 205}, + {0x801f, 205}, + {0x8029, 205}, + {0xc038, 205}, + }, + /* 198 */ + { + {0x8003, 210}, + {0x8006, 210}, + {0x800a, 210}, + {0x800f, 210}, + {0x8018, 210}, + {0x801f, 210}, + {0x8029, 210}, + {0xc038, 210}, + {0x8003, 213}, + {0x8006, 213}, + {0x800a, 213}, + {0x800f, 213}, + {0x8018, 213}, + {0x801f, 213}, + {0x8029, 213}, + {0xc038, 213}, + }, + /* 199 */ + { + {0x8001, 218}, + {0xc016, 218}, + {0x8001, 219}, + {0xc016, 219}, + {0x8001, 238}, + {0xc016, 238}, + {0x8001, 240}, + {0xc016, 240}, + {0x8001, 242}, + {0xc016, 242}, + {0x8001, 243}, + {0xc016, 243}, + {0x8001, 255}, + {0xc016, 255}, + {0xc000, 203}, + {0xc000, 204}, + }, + /* 200 */ + { + {0x8002, 218}, + {0x8009, 218}, + {0x8017, 218}, + {0xc028, 218}, + {0x8002, 219}, + {0x8009, 219}, + {0x8017, 219}, + {0xc028, 219}, + {0x8002, 238}, + {0x8009, 238}, + {0x8017, 238}, + {0xc028, 238}, + {0x8002, 240}, + {0x8009, 240}, + {0x8017, 240}, + {0xc028, 240}, + }, + /* 201 */ + { + {0x8003, 218}, + {0x8006, 218}, + {0x800a, 218}, + {0x800f, 218}, + {0x8018, 218}, + {0x801f, 218}, + {0x8029, 218}, + {0xc038, 218}, + {0x8003, 219}, + {0x8006, 219}, + {0x800a, 219}, + {0x800f, 219}, + {0x8018, 219}, + {0x801f, 219}, + {0x8029, 219}, + {0xc038, 219}, + }, + /* 202 */ + { + {0x8003, 238}, + {0x8006, 238}, + {0x800a, 238}, + {0x800f, 238}, + {0x8018, 238}, + {0x801f, 238}, + {0x8029, 238}, + {0xc038, 238}, + {0x8003, 240}, + {0x8006, 240}, + {0x800a, 240}, + {0x800f, 240}, + {0x8018, 240}, + {0x801f, 240}, + {0x8029, 240}, + {0xc038, 240}, + }, + /* 203 */ + { + {0x8002, 242}, + {0x8009, 242}, + {0x8017, 242}, + {0xc028, 242}, + {0x8002, 243}, + {0x8009, 243}, + {0x8017, 243}, + {0xc028, 243}, + {0x8002, 255}, + {0x8009, 255}, + {0x8017, 255}, + {0xc028, 255}, + {0x8001, 203}, + {0xc016, 203}, + {0x8001, 204}, + {0xc016, 204}, + }, + /* 204 */ + { + {0x8003, 242}, + {0x8006, 242}, + {0x800a, 242}, + {0x800f, 242}, + {0x8018, 242}, + {0x801f, 242}, + {0x8029, 242}, + {0xc038, 242}, + {0x8003, 243}, + {0x8006, 243}, + {0x800a, 243}, + {0x800f, 243}, + {0x8018, 243}, + {0x801f, 243}, + {0x8029, 243}, + {0xc038, 243}, + }, + /* 205 */ + { + {0x8003, 255}, + {0x8006, 255}, + {0x800a, 255}, + {0x800f, 255}, + {0x8018, 255}, + {0x801f, 255}, + {0x8029, 255}, + {0xc038, 255}, + {0x8002, 203}, + {0x8009, 203}, + {0x8017, 203}, + {0xc028, 203}, + {0x8002, 204}, + {0x8009, 204}, + {0x8017, 204}, + {0xc028, 204}, + }, + /* 206 */ + { + {0x8003, 203}, + {0x8006, 203}, + {0x800a, 203}, + {0x800f, 203}, + {0x8018, 203}, + {0x801f, 203}, + {0x8029, 203}, + {0xc038, 203}, + {0x8003, 204}, + {0x8006, 204}, + {0x800a, 204}, + {0x800f, 204}, + {0x8018, 204}, + {0x801f, 204}, + {0x8029, 204}, + {0xc038, 204}, + }, + /* 207 */ + { + {0xd3, 0}, + {0xd4, 0}, + {0xd6, 0}, + {0xd7, 0}, + {0xda, 0}, + {0xdb, 0}, + {0xdd, 0}, + {0xde, 0}, + {0xe2, 0}, + {0xe4, 0}, + {0xe8, 0}, + {0xeb, 0}, + {0xf0, 0}, + {0xf3, 0}, + {0xf7, 0}, + {0xfa, 0}, + }, + /* 208 */ + { + {0xc000, 211}, + {0xc000, 212}, + {0xc000, 214}, + {0xc000, 221}, + {0xc000, 222}, + {0xc000, 223}, + {0xc000, 241}, + {0xc000, 244}, + {0xc000, 245}, + {0xc000, 246}, + {0xc000, 247}, + {0xc000, 248}, + {0xc000, 250}, + {0xc000, 251}, + {0xc000, 252}, + {0xc000, 253}, + }, + /* 209 */ + { + {0x8001, 211}, + {0xc016, 211}, + {0x8001, 212}, + {0xc016, 212}, + {0x8001, 214}, + {0xc016, 214}, + {0x8001, 221}, + {0xc016, 221}, + {0x8001, 222}, + {0xc016, 222}, + {0x8001, 223}, + {0xc016, 223}, + {0x8001, 241}, + {0xc016, 241}, + {0x8001, 244}, + {0xc016, 244}, + }, + /* 210 */ + { + {0x8002, 211}, + {0x8009, 211}, + {0x8017, 211}, + {0xc028, 211}, + {0x8002, 212}, + {0x8009, 212}, + {0x8017, 212}, + {0xc028, 212}, + {0x8002, 214}, + {0x8009, 214}, + {0x8017, 214}, + {0xc028, 214}, + {0x8002, 221}, + {0x8009, 221}, + {0x8017, 221}, + {0xc028, 221}, + }, + /* 211 */ + { + {0x8003, 211}, + {0x8006, 211}, + {0x800a, 211}, + {0x800f, 211}, + {0x8018, 211}, + {0x801f, 211}, + {0x8029, 211}, + {0xc038, 211}, + {0x8003, 212}, + {0x8006, 212}, + {0x800a, 212}, + {0x800f, 212}, + {0x8018, 212}, + {0x801f, 212}, + {0x8029, 212}, + {0xc038, 212}, + }, + /* 212 */ + { + {0x8003, 214}, + {0x8006, 214}, + {0x800a, 214}, + {0x800f, 214}, + {0x8018, 214}, + {0x801f, 214}, + {0x8029, 214}, + {0xc038, 214}, + {0x8003, 221}, + {0x8006, 221}, + {0x800a, 221}, + {0x800f, 221}, + {0x8018, 221}, + {0x801f, 221}, + {0x8029, 221}, + {0xc038, 221}, + }, + /* 213 */ + { + {0x8002, 222}, + {0x8009, 222}, + {0x8017, 222}, + {0xc028, 222}, + {0x8002, 223}, + {0x8009, 223}, + {0x8017, 223}, + {0xc028, 223}, + {0x8002, 241}, + {0x8009, 241}, + {0x8017, 241}, + {0xc028, 241}, + {0x8002, 244}, + {0x8009, 244}, + {0x8017, 244}, + {0xc028, 244}, + }, + /* 214 */ + { + {0x8003, 222}, + {0x8006, 222}, + {0x800a, 222}, + {0x800f, 222}, + {0x8018, 222}, + {0x801f, 222}, + {0x8029, 222}, + {0xc038, 222}, + {0x8003, 223}, + {0x8006, 223}, + {0x800a, 223}, + {0x800f, 223}, + {0x8018, 223}, + {0x801f, 223}, + {0x8029, 223}, + {0xc038, 223}, + }, + /* 215 */ + { + {0x8003, 241}, + {0x8006, 241}, + {0x800a, 241}, + {0x800f, 241}, + {0x8018, 241}, + {0x801f, 241}, + {0x8029, 241}, + {0xc038, 241}, + {0x8003, 244}, + {0x8006, 244}, + {0x800a, 244}, + {0x800f, 244}, + {0x8018, 244}, + {0x801f, 244}, + {0x8029, 244}, + {0xc038, 244}, + }, + /* 216 */ + { + {0x8001, 245}, + {0xc016, 245}, + {0x8001, 246}, + {0xc016, 246}, + {0x8001, 247}, + {0xc016, 247}, + {0x8001, 248}, + {0xc016, 248}, + {0x8001, 250}, + {0xc016, 250}, + {0x8001, 251}, + {0xc016, 251}, + {0x8001, 252}, + {0xc016, 252}, + {0x8001, 253}, + {0xc016, 253}, + }, + /* 217 */ + { + {0x8002, 245}, + {0x8009, 245}, + {0x8017, 245}, + {0xc028, 245}, + {0x8002, 246}, + {0x8009, 246}, + {0x8017, 246}, + {0xc028, 246}, + {0x8002, 247}, + {0x8009, 247}, + {0x8017, 247}, + {0xc028, 247}, + {0x8002, 248}, + {0x8009, 248}, + {0x8017, 248}, + {0xc028, 248}, + }, + /* 218 */ + { + {0x8003, 245}, + {0x8006, 245}, + {0x800a, 245}, + {0x800f, 245}, + {0x8018, 245}, + {0x801f, 245}, + {0x8029, 245}, + {0xc038, 245}, + {0x8003, 246}, + {0x8006, 246}, + {0x800a, 246}, + {0x800f, 246}, + {0x8018, 246}, + {0x801f, 246}, + {0x8029, 246}, + {0xc038, 246}, + }, + /* 219 */ + { + {0x8003, 247}, + {0x8006, 247}, + {0x800a, 247}, + {0x800f, 247}, + {0x8018, 247}, + {0x801f, 247}, + {0x8029, 247}, + {0xc038, 247}, + {0x8003, 248}, + {0x8006, 248}, + {0x800a, 248}, + {0x800f, 248}, + {0x8018, 248}, + {0x801f, 248}, + {0x8029, 248}, + {0xc038, 248}, + }, + /* 220 */ + { + {0x8002, 250}, + {0x8009, 250}, + {0x8017, 250}, + {0xc028, 250}, + {0x8002, 251}, + {0x8009, 251}, + {0x8017, 251}, + {0xc028, 251}, + {0x8002, 252}, + {0x8009, 252}, + {0x8017, 252}, + {0xc028, 252}, + {0x8002, 253}, + {0x8009, 253}, + {0x8017, 253}, + {0xc028, 253}, + }, + /* 221 */ + { + {0x8003, 250}, + {0x8006, 250}, + {0x800a, 250}, + {0x800f, 250}, + {0x8018, 250}, + {0x801f, 250}, + {0x8029, 250}, + {0xc038, 250}, + {0x8003, 251}, + {0x8006, 251}, + {0x800a, 251}, + {0x800f, 251}, + {0x8018, 251}, + {0x801f, 251}, + {0x8029, 251}, + {0xc038, 251}, + }, + /* 222 */ + { + {0x8003, 252}, + {0x8006, 252}, + {0x800a, 252}, + {0x800f, 252}, + {0x8018, 252}, + {0x801f, 252}, + {0x8029, 252}, + {0xc038, 252}, + {0x8003, 253}, + {0x8006, 253}, + {0x800a, 253}, + {0x800f, 253}, + {0x8018, 253}, + {0x801f, 253}, + {0x8029, 253}, + {0xc038, 253}, + }, + /* 223 */ + { + {0xc000, 254}, + {0xe3, 0}, + {0xe5, 0}, + {0xe6, 0}, + {0xe9, 0}, + {0xea, 0}, + {0xec, 0}, + {0xed, 0}, + {0xf1, 0}, + {0xf2, 0}, + {0xf4, 0}, + {0xf5, 0}, + {0xf8, 0}, + {0xf9, 0}, + {0xfb, 0}, + {0xfc, 0}, + }, + /* 224 */ + { + {0x8001, 254}, + {0xc016, 254}, + {0xc000, 2}, + {0xc000, 3}, + {0xc000, 4}, + {0xc000, 5}, + {0xc000, 6}, + {0xc000, 7}, + {0xc000, 8}, + {0xc000, 11}, + {0xc000, 12}, + {0xc000, 14}, + {0xc000, 15}, + {0xc000, 16}, + {0xc000, 17}, + {0xc000, 18}, + }, + /* 225 */ + { + {0x8002, 254}, + {0x8009, 254}, + {0x8017, 254}, + {0xc028, 254}, + {0x8001, 2}, + {0xc016, 2}, + {0x8001, 3}, + {0xc016, 3}, + {0x8001, 4}, + {0xc016, 4}, + {0x8001, 5}, + {0xc016, 5}, + {0x8001, 6}, + {0xc016, 6}, + {0x8001, 7}, + {0xc016, 7}, + }, + /* 226 */ + { + {0x8003, 254}, + {0x8006, 254}, + {0x800a, 254}, + {0x800f, 254}, + {0x8018, 254}, + {0x801f, 254}, + {0x8029, 254}, + {0xc038, 254}, + {0x8002, 2}, + {0x8009, 2}, + {0x8017, 2}, + {0xc028, 2}, + {0x8002, 3}, + {0x8009, 3}, + {0x8017, 3}, + {0xc028, 3}, + }, + /* 227 */ + { + {0x8003, 2}, + {0x8006, 2}, + {0x800a, 2}, + {0x800f, 2}, + {0x8018, 2}, + {0x801f, 2}, + {0x8029, 2}, + {0xc038, 2}, + {0x8003, 3}, + {0x8006, 3}, + {0x800a, 3}, + {0x800f, 3}, + {0x8018, 3}, + {0x801f, 3}, + {0x8029, 3}, + {0xc038, 3}, + }, + /* 228 */ + { + {0x8002, 4}, + {0x8009, 4}, + {0x8017, 4}, + {0xc028, 4}, + {0x8002, 5}, + {0x8009, 5}, + {0x8017, 5}, + {0xc028, 5}, + {0x8002, 6}, + {0x8009, 6}, + {0x8017, 6}, + {0xc028, 6}, + {0x8002, 7}, + {0x8009, 7}, + {0x8017, 7}, + {0xc028, 7}, + }, + /* 229 */ + { + {0x8003, 4}, + {0x8006, 4}, + {0x800a, 4}, + {0x800f, 4}, + {0x8018, 4}, + {0x801f, 4}, + {0x8029, 4}, + {0xc038, 4}, + {0x8003, 5}, + {0x8006, 5}, + {0x800a, 5}, + {0x800f, 5}, + {0x8018, 5}, + {0x801f, 5}, + {0x8029, 5}, + {0xc038, 5}, + }, + /* 230 */ + { + {0x8003, 6}, + {0x8006, 6}, + {0x800a, 6}, + {0x800f, 6}, + {0x8018, 6}, + {0x801f, 6}, + {0x8029, 6}, + {0xc038, 6}, + {0x8003, 7}, + {0x8006, 7}, + {0x800a, 7}, + {0x800f, 7}, + {0x8018, 7}, + {0x801f, 7}, + {0x8029, 7}, + {0xc038, 7}, + }, + /* 231 */ + { + {0x8001, 8}, + {0xc016, 8}, + {0x8001, 11}, + {0xc016, 11}, + {0x8001, 12}, + {0xc016, 12}, + {0x8001, 14}, + {0xc016, 14}, + {0x8001, 15}, + {0xc016, 15}, + {0x8001, 16}, + {0xc016, 16}, + {0x8001, 17}, + {0xc016, 17}, + {0x8001, 18}, + {0xc016, 18}, + }, + /* 232 */ + { + {0x8002, 8}, + {0x8009, 8}, + {0x8017, 8}, + {0xc028, 8}, + {0x8002, 11}, + {0x8009, 11}, + {0x8017, 11}, + {0xc028, 11}, + {0x8002, 12}, + {0x8009, 12}, + {0x8017, 12}, + {0xc028, 12}, + {0x8002, 14}, + {0x8009, 14}, + {0x8017, 14}, + {0xc028, 14}, + }, + /* 233 */ + { + {0x8003, 8}, + {0x8006, 8}, + {0x800a, 8}, + {0x800f, 8}, + {0x8018, 8}, + {0x801f, 8}, + {0x8029, 8}, + {0xc038, 8}, + {0x8003, 11}, + {0x8006, 11}, + {0x800a, 11}, + {0x800f, 11}, + {0x8018, 11}, + {0x801f, 11}, + {0x8029, 11}, + {0xc038, 11}, + }, + /* 234 */ + { + {0x8003, 12}, + {0x8006, 12}, + {0x800a, 12}, + {0x800f, 12}, + {0x8018, 12}, + {0x801f, 12}, + {0x8029, 12}, + {0xc038, 12}, + {0x8003, 14}, + {0x8006, 14}, + {0x800a, 14}, + {0x800f, 14}, + {0x8018, 14}, + {0x801f, 14}, + {0x8029, 14}, + {0xc038, 14}, + }, + /* 235 */ + { + {0x8002, 15}, + {0x8009, 15}, + {0x8017, 15}, + {0xc028, 15}, + {0x8002, 16}, + {0x8009, 16}, + {0x8017, 16}, + {0xc028, 16}, + {0x8002, 17}, + {0x8009, 17}, + {0x8017, 17}, + {0xc028, 17}, + {0x8002, 18}, + {0x8009, 18}, + {0x8017, 18}, + {0xc028, 18}, + }, + /* 236 */ + { + {0x8003, 15}, + {0x8006, 15}, + {0x800a, 15}, + {0x800f, 15}, + {0x8018, 15}, + {0x801f, 15}, + {0x8029, 15}, + {0xc038, 15}, + {0x8003, 16}, + {0x8006, 16}, + {0x800a, 16}, + {0x800f, 16}, + {0x8018, 16}, + {0x801f, 16}, + {0x8029, 16}, + {0xc038, 16}, + }, + /* 237 */ + { + {0x8003, 17}, + {0x8006, 17}, + {0x800a, 17}, + {0x800f, 17}, + {0x8018, 17}, + {0x801f, 17}, + {0x8029, 17}, + {0xc038, 17}, + {0x8003, 18}, + {0x8006, 18}, + {0x800a, 18}, + {0x800f, 18}, + {0x8018, 18}, + {0x801f, 18}, + {0x8029, 18}, + {0xc038, 18}, + }, + /* 238 */ + { + {0xc000, 19}, + {0xc000, 20}, + {0xc000, 21}, + {0xc000, 23}, + {0xc000, 24}, + {0xc000, 25}, + {0xc000, 26}, + {0xc000, 27}, + {0xc000, 28}, + {0xc000, 29}, + {0xc000, 30}, + {0xc000, 31}, + {0xc000, 127}, + {0xc000, 220}, + {0xc000, 249}, + {0xfd, 0}, + }, + /* 239 */ + { + {0x8001, 19}, + {0xc016, 19}, + {0x8001, 20}, + {0xc016, 20}, + {0x8001, 21}, + {0xc016, 21}, + {0x8001, 23}, + {0xc016, 23}, + {0x8001, 24}, + {0xc016, 24}, + {0x8001, 25}, + {0xc016, 25}, + {0x8001, 26}, + {0xc016, 26}, + {0x8001, 27}, + {0xc016, 27}, + }, + /* 240 */ + { + {0x8002, 19}, + {0x8009, 19}, + {0x8017, 19}, + {0xc028, 19}, + {0x8002, 20}, + {0x8009, 20}, + {0x8017, 20}, + {0xc028, 20}, + {0x8002, 21}, + {0x8009, 21}, + {0x8017, 21}, + {0xc028, 21}, + {0x8002, 23}, + {0x8009, 23}, + {0x8017, 23}, + {0xc028, 23}, + }, + /* 241 */ + { + {0x8003, 19}, + {0x8006, 19}, + {0x800a, 19}, + {0x800f, 19}, + {0x8018, 19}, + {0x801f, 19}, + {0x8029, 19}, + {0xc038, 19}, + {0x8003, 20}, + {0x8006, 20}, + {0x800a, 20}, + {0x800f, 20}, + {0x8018, 20}, + {0x801f, 20}, + {0x8029, 20}, + {0xc038, 20}, + }, + /* 242 */ + { + {0x8003, 21}, + {0x8006, 21}, + {0x800a, 21}, + {0x800f, 21}, + {0x8018, 21}, + {0x801f, 21}, + {0x8029, 21}, + {0xc038, 21}, + {0x8003, 23}, + {0x8006, 23}, + {0x800a, 23}, + {0x800f, 23}, + {0x8018, 23}, + {0x801f, 23}, + {0x8029, 23}, + {0xc038, 23}, + }, + /* 243 */ + { + {0x8002, 24}, + {0x8009, 24}, + {0x8017, 24}, + {0xc028, 24}, + {0x8002, 25}, + {0x8009, 25}, + {0x8017, 25}, + {0xc028, 25}, + {0x8002, 26}, + {0x8009, 26}, + {0x8017, 26}, + {0xc028, 26}, + {0x8002, 27}, + {0x8009, 27}, + {0x8017, 27}, + {0xc028, 27}, + }, + /* 244 */ + { + {0x8003, 24}, + {0x8006, 24}, + {0x800a, 24}, + {0x800f, 24}, + {0x8018, 24}, + {0x801f, 24}, + {0x8029, 24}, + {0xc038, 24}, + {0x8003, 25}, + {0x8006, 25}, + {0x800a, 25}, + {0x800f, 25}, + {0x8018, 25}, + {0x801f, 25}, + {0x8029, 25}, + {0xc038, 25}, + }, + /* 245 */ + { + {0x8003, 26}, + {0x8006, 26}, + {0x800a, 26}, + {0x800f, 26}, + {0x8018, 26}, + {0x801f, 26}, + {0x8029, 26}, + {0xc038, 26}, + {0x8003, 27}, + {0x8006, 27}, + {0x800a, 27}, + {0x800f, 27}, + {0x8018, 27}, + {0x801f, 27}, + {0x8029, 27}, + {0xc038, 27}, + }, + /* 246 */ + { + {0x8001, 28}, + {0xc016, 28}, + {0x8001, 29}, + {0xc016, 29}, + {0x8001, 30}, + {0xc016, 30}, + {0x8001, 31}, + {0xc016, 31}, + {0x8001, 127}, + {0xc016, 127}, + {0x8001, 220}, + {0xc016, 220}, + {0x8001, 249}, + {0xc016, 249}, + {0xfe, 0}, + {0xff, 0}, + }, + /* 247 */ + { + {0x8002, 28}, + {0x8009, 28}, + {0x8017, 28}, + {0xc028, 28}, + {0x8002, 29}, + {0x8009, 29}, + {0x8017, 29}, + {0xc028, 29}, + {0x8002, 30}, + {0x8009, 30}, + {0x8017, 30}, + {0xc028, 30}, + {0x8002, 31}, + {0x8009, 31}, + {0x8017, 31}, + {0xc028, 31}, + }, + /* 248 */ + { + {0x8003, 28}, + {0x8006, 28}, + {0x800a, 28}, + {0x800f, 28}, + {0x8018, 28}, + {0x801f, 28}, + {0x8029, 28}, + {0xc038, 28}, + {0x8003, 29}, + {0x8006, 29}, + {0x800a, 29}, + {0x800f, 29}, + {0x8018, 29}, + {0x801f, 29}, + {0x8029, 29}, + {0xc038, 29}, + }, + /* 249 */ + { + {0x8003, 30}, + {0x8006, 30}, + {0x800a, 30}, + {0x800f, 30}, + {0x8018, 30}, + {0x801f, 30}, + {0x8029, 30}, + {0xc038, 30}, + {0x8003, 31}, + {0x8006, 31}, + {0x800a, 31}, + {0x800f, 31}, + {0x8018, 31}, + {0x801f, 31}, + {0x8029, 31}, + {0xc038, 31}, + }, + /* 250 */ + { + {0x8002, 127}, + {0x8009, 127}, + {0x8017, 127}, + {0xc028, 127}, + {0x8002, 220}, + {0x8009, 220}, + {0x8017, 220}, + {0xc028, 220}, + {0x8002, 249}, + {0x8009, 249}, + {0x8017, 249}, + {0xc028, 249}, + {0xc000, 10}, + {0xc000, 13}, + {0xc000, 22}, + {0x100, 0}, + }, + /* 251 */ + { + {0x8003, 127}, + {0x8006, 127}, + {0x800a, 127}, + {0x800f, 127}, + {0x8018, 127}, + {0x801f, 127}, + {0x8029, 127}, + {0xc038, 127}, + {0x8003, 220}, + {0x8006, 220}, + {0x800a, 220}, + {0x800f, 220}, + {0x8018, 220}, + {0x801f, 220}, + {0x8029, 220}, + {0xc038, 220}, + }, + /* 252 */ + { + {0x8003, 249}, + {0x8006, 249}, + {0x800a, 249}, + {0x800f, 249}, + {0x8018, 249}, + {0x801f, 249}, + {0x8029, 249}, + {0xc038, 249}, + {0x8001, 10}, + {0xc016, 10}, + {0x8001, 13}, + {0xc016, 13}, + {0x8001, 22}, + {0xc016, 22}, + {0x100, 0}, + {0x100, 0}, + }, + /* 253 */ + { + {0x8002, 10}, + {0x8009, 10}, + {0x8017, 10}, + {0xc028, 10}, + {0x8002, 13}, + {0x8009, 13}, + {0x8017, 13}, + {0xc028, 13}, + {0x8002, 22}, + {0x8009, 22}, + {0x8017, 22}, + {0xc028, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 254 */ + { + {0x8003, 10}, + {0x8006, 10}, + {0x800a, 10}, + {0x800f, 10}, + {0x8018, 10}, + {0x801f, 10}, + {0x8029, 10}, + {0xc038, 10}, + {0x8003, 13}, + {0x8006, 13}, + {0x800a, 13}, + {0x800f, 13}, + {0x8018, 13}, + {0x801f, 13}, + {0x8029, 13}, + {0xc038, 13}, + }, + /* 255 */ + { + {0x8003, 22}, + {0x8006, 22}, + {0x800a, 22}, + {0x800f, 22}, + {0x8018, 22}, + {0x801f, 22}, + {0x8029, 22}, + {0xc038, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 256 */ + { + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, +}; diff --git a/Utilities/cmnghttp2/lib/nghttp2_helper.c b/Utilities/cmnghttp2/lib/nghttp2_helper.c new file mode 100644 index 00000000000..91136a61986 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_helper.c @@ -0,0 +1,629 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_helper.h" + +#include +#include + +#include "nghttp2_net.h" + +void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) { + uint16_t x = htons(n); + memcpy(buf, &x, sizeof(uint16_t)); +} + +void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) { + uint32_t x = htonl(n); + memcpy(buf, &x, sizeof(uint32_t)); +} + +uint16_t nghttp2_get_uint16(const uint8_t *data) { + uint16_t n; + memcpy(&n, data, sizeof(uint16_t)); + return ntohs(n); +} + +uint32_t nghttp2_get_uint32(const uint8_t *data) { + uint32_t n; + memcpy(&n, data, sizeof(uint32_t)); + return ntohl(n); +} + +/* Generated by gendowncasetbl.py */ +static const uint8_t DOWNCASE_TBL[] = { + 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, + 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, + 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, + 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, + 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, + 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, + 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, + 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, + 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, + 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, + 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, + 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, + 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, + 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, + 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, + 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, + 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, + 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, + 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, + 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, + 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, + 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, + 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, + 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, + 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, + 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, + 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, + 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, + 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, + 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, + 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, + 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, + 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, + 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, + 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, + 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, + 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, + 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, + 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, + 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, + 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, + 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, + 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, + 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, + 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, + 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, + 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, + 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, + 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, + 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, + 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, + 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, + 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, + 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, + 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, + 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, + 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, + 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, + 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, + 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, + 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, + 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, + 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, + 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, +}; + +void nghttp2_downcase(uint8_t *s, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + s[i] = DOWNCASE_TBL[s[i]]; + } +} + +/* + * local_window_size + * ^ * + * | * recv_window_size + * | * * ^ + * | * * | + * 0+++++++++ + * | * * \ + * | * * | This rage is hidden in flow control. But it must be + * v * * / kept in order to restore it when window size is enlarged. + * recv_reduction + * (+ for negative direction) + * + * recv_window_size could be negative if we decrease + * local_window_size more than recv_window_size: + * + * local_window_size + * ^ * + * | * + * | * + * 0++++++++ + * | * ^ recv_window_size (negative) + * | * | + * v * * + * recv_reduction + */ +int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr) { + if (*delta_ptr > 0) { + int32_t recv_reduction_delta; + int32_t delta; + int32_t new_recv_window_size = + nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr; + + if (new_recv_window_size >= 0) { + *recv_window_size_ptr = new_recv_window_size; + return 0; + } + + delta = -new_recv_window_size; + + /* The delta size is strictly more than received bytes. Increase + local_window_size by that difference |delta|. */ + if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + *local_window_size_ptr += delta; + /* If there is recv_reduction due to earlier window_size + reduction, we have to adjust it too. */ + recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta); + *recv_reduction_ptr -= recv_reduction_delta; + if (*recv_window_size_ptr < 0) { + *recv_window_size_ptr += recv_reduction_delta; + } else { + /* If *recv_window_size_ptr > 0, then those bytes are going to + be returned to the remote peer (by WINDOW_UPDATE with the + adjusted *delta_ptr), so it is effectively 0 now. We set to + *recv_reduction_delta, because caller does not take into + account it in *delta_ptr. */ + *recv_window_size_ptr = recv_reduction_delta; + } + /* recv_reduction_delta must be paid from *delta_ptr, since it was + added in window size reduction (see below). */ + *delta_ptr -= recv_reduction_delta; + + return 0; + } + + if (*local_window_size_ptr + *delta_ptr < 0 || + *recv_window_size_ptr < INT32_MIN - *delta_ptr || + *recv_reduction_ptr > INT32_MAX + *delta_ptr) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + /* Decreasing local window size. Note that we achieve this without + noticing to the remote peer. To do this, we cut + recv_window_size by -delta. This means that we don't send + WINDOW_UPDATE for -delta bytes. */ + *local_window_size_ptr += *delta_ptr; + *recv_window_size_ptr += *delta_ptr; + *recv_reduction_ptr -= *delta_ptr; + *delta_ptr = 0; + + return 0; +} + +int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr) { + int32_t recv_reduction_delta; + int32_t delta; + + delta = *delta_ptr; + + assert(delta >= 0); + + /* The delta size is strictly more than received bytes. Increase + local_window_size by that difference |delta|. */ + if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + + *local_window_size_ptr += delta; + /* If there is recv_reduction due to earlier window_size + reduction, we have to adjust it too. */ + recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta); + *recv_reduction_ptr -= recv_reduction_delta; + + *recv_window_size_ptr += recv_reduction_delta; + + /* recv_reduction_delta must be paid from *delta_ptr, since it was + added in window size reduction (see below). */ + *delta_ptr -= recv_reduction_delta; + + return 0; +} + +int nghttp2_should_send_window_update(int32_t local_window_size, + int32_t recv_window_size) { + return recv_window_size > 0 && recv_window_size >= local_window_size / 2; +} + +const char *nghttp2_strerror(int error_code) { + switch (error_code) { + case 0: + return "Success"; + case NGHTTP2_ERR_INVALID_ARGUMENT: + return "Invalid argument"; + case NGHTTP2_ERR_BUFFER_ERROR: + return "Out of buffer space"; + case NGHTTP2_ERR_UNSUPPORTED_VERSION: + return "Unsupported SPDY version"; + case NGHTTP2_ERR_WOULDBLOCK: + return "Operation would block"; + case NGHTTP2_ERR_PROTO: + return "Protocol error"; + case NGHTTP2_ERR_INVALID_FRAME: + return "Invalid frame octets"; + case NGHTTP2_ERR_EOF: + return "EOF"; + case NGHTTP2_ERR_DEFERRED: + return "Data transfer deferred"; + case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: + return "No more Stream ID available"; + case NGHTTP2_ERR_STREAM_CLOSED: + return "Stream was already closed or invalid"; + case NGHTTP2_ERR_STREAM_CLOSING: + return "Stream is closing"; + case NGHTTP2_ERR_STREAM_SHUT_WR: + return "The transmission is not allowed for this stream"; + case NGHTTP2_ERR_INVALID_STREAM_ID: + return "Stream ID is invalid"; + case NGHTTP2_ERR_INVALID_STREAM_STATE: + return "Invalid stream state"; + case NGHTTP2_ERR_DEFERRED_DATA_EXIST: + return "Another DATA frame has already been deferred"; + case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED: + return "request HEADERS is not allowed"; + case NGHTTP2_ERR_GOAWAY_ALREADY_SENT: + return "GOAWAY has already been sent"; + case NGHTTP2_ERR_INVALID_HEADER_BLOCK: + return "Invalid header block"; + case NGHTTP2_ERR_INVALID_STATE: + return "Invalid state"; + case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: + return "The user callback function failed due to the temporal error"; + case NGHTTP2_ERR_FRAME_SIZE_ERROR: + return "The length of the frame is invalid"; + case NGHTTP2_ERR_HEADER_COMP: + return "Header compression/decompression error"; + case NGHTTP2_ERR_FLOW_CONTROL: + return "Flow control error"; + case NGHTTP2_ERR_INSUFF_BUFSIZE: + return "Insufficient buffer size given to function"; + case NGHTTP2_ERR_PAUSE: + return "Callback was paused by the application"; + case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS: + return "Too many inflight SETTINGS"; + case NGHTTP2_ERR_PUSH_DISABLED: + return "Server push is disabled by peer"; + case NGHTTP2_ERR_DATA_EXIST: + return "DATA or HEADERS frame has already been submitted for the stream"; + case NGHTTP2_ERR_SESSION_CLOSING: + return "The current session is closing"; + case NGHTTP2_ERR_HTTP_HEADER: + return "Invalid HTTP header field was received"; + case NGHTTP2_ERR_HTTP_MESSAGING: + return "Violation in HTTP messaging rule"; + case NGHTTP2_ERR_REFUSED_STREAM: + return "Stream was refused"; + case NGHTTP2_ERR_INTERNAL: + return "Internal error"; + case NGHTTP2_ERR_CANCEL: + return "Cancel"; + case NGHTTP2_ERR_SETTINGS_EXPECTED: + return "When a local endpoint expects to receive SETTINGS frame, it " + "receives an other type of frame"; + case NGHTTP2_ERR_NOMEM: + return "Out of memory"; + case NGHTTP2_ERR_CALLBACK_FAILURE: + return "The user callback function failed"; + case NGHTTP2_ERR_BAD_CLIENT_MAGIC: + return "Received bad client magic byte string"; + case NGHTTP2_ERR_FLOODED: + return "Flooding was detected in this HTTP/2 session, and it must be " + "closed"; + default: + return "Unknown error code"; + } +} + +/* Generated by gennmchartbl.py */ +static const int VALID_HD_NAME_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, + 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, + 0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */, + 0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */, + 0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */, + 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, + 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, + 0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */, + 0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */, + 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, + 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, + 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, + 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, + 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, + 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, + 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, + 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, + 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, + 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, + 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, + 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, + 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, + 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, + 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, + 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, + 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, + 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, + 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, + 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, + 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, + 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, + 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, + 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, + 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, + 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, + 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, + 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ +}; + +int nghttp2_check_header_name(const uint8_t *name, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + if (*name == ':') { + if (len == 1) { + return 0; + } + ++name; + --len; + } + for (last = name + len; name != last; ++name) { + if (!VALID_HD_NAME_CHARS[*name]) { + return 0; + } + } + return 1; +} + +/* Generated by genvchartbl.py */ +static const int VALID_HD_VALUE_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, + 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, + 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, + 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, + 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, + 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, + 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, + 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, + 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, + 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, + 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, + 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, + 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, + 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, + 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, + 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, + 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, + 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, + 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, + 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, + 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, + 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, + 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, + 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, + 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, + 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, + 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, + 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, + 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, + 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, + 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, + 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, + 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, + 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, + 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, + 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, + 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ +}; + +int nghttp2_check_header_value(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_HD_VALUE_CHARS[*value]) { + return 0; + } + } + return 1; +} + +/* Generated by genauthroitychartbl.py */ +static char VALID_AUTHORITY_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 0 /* " */, 0 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, + 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, + 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, + 0 /* \ */, 1 /* ] */, 0 /* ^ */, 1 /* _ */, + 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, + 0 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, + 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, + 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, + 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, + 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, + 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, + 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, + 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, + 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, + 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, + 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, + 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, + 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, + 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, + 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, + 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, + 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, + 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, + 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, + 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, + 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, + 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, + 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, + 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, + 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, + 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, + 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ +}; + +int nghttp2_check_authority(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_AUTHORITY_CHARS[*value]) { + return 0; + } + } + return 1; +} + +uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) { + if (len == 0) { + return dest; + } + + memcpy(dest, src, len); + + return dest + len; +} + +const char *nghttp2_http2_strerror(uint32_t error_code) { + switch (error_code) { + case NGHTTP2_NO_ERROR: + return "NO_ERROR"; + case NGHTTP2_PROTOCOL_ERROR: + return "PROTOCOL_ERROR"; + case NGHTTP2_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case NGHTTP2_FLOW_CONTROL_ERROR: + return "FLOW_CONTROL_ERROR"; + case NGHTTP2_SETTINGS_TIMEOUT: + return "SETTINGS_TIMEOUT"; + case NGHTTP2_STREAM_CLOSED: + return "STREAM_CLOSED"; + case NGHTTP2_FRAME_SIZE_ERROR: + return "FRAME_SIZE_ERROR"; + case NGHTTP2_REFUSED_STREAM: + return "REFUSED_STREAM"; + case NGHTTP2_CANCEL: + return "CANCEL"; + case NGHTTP2_COMPRESSION_ERROR: + return "COMPRESSION_ERROR"; + case NGHTTP2_CONNECT_ERROR: + return "CONNECT_ERROR"; + case NGHTTP2_ENHANCE_YOUR_CALM: + return "ENHANCE_YOUR_CALM"; + case NGHTTP2_INADEQUATE_SECURITY: + return "INADEQUATE_SECURITY"; + case NGHTTP2_HTTP_1_1_REQUIRED: + return "HTTP_1_1_REQUIRED"; + default: + return "unknown"; + } +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_helper.h b/Utilities/cmnghttp2/lib/nghttp2_helper.h new file mode 100644 index 00000000000..b1f18ce541a --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_helper.h @@ -0,0 +1,122 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HELPER_H +#define NGHTTP2_HELPER_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include +#include "nghttp2_mem.h" + +#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) +#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) + +#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) + +#define nghttp2_struct_of(ptr, type, member) \ + ((type *)(void *)((char *)(ptr)-offsetof(type, member))) + +/* + * Copies 2 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void nghttp2_put_uint16be(uint8_t *buf, uint16_t n); + +/* + * Copies 4 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void nghttp2_put_uint32be(uint8_t *buf, uint32_t n); + +/* + * Retrieves 2 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint16_t nghttp2_get_uint16(const uint8_t *data); + +/* + * Retrieves 4 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint32_t nghttp2_get_uint32(const uint8_t *data); + +void nghttp2_downcase(uint8_t *s, size_t len); + +/* + * Adjusts |*local_window_size_ptr|, |*recv_window_size_ptr|, + * |*recv_reduction_ptr| with |*delta_ptr| which is the + * WINDOW_UPDATE's window_size_increment sent from local side. If + * |delta| is strictly larger than |*recv_window_size_ptr|, + * |*local_window_size_ptr| is increased by delta - + * *recv_window_size_ptr. If |delta| is negative, + * |*local_window_size_ptr| is decreased by delta. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_FLOW_CONTROL + * local_window_size overflow or gets negative. + */ +int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr); + +/* + * This function works like nghttp2_adjust_local_window_size(). The + * difference is that this function assumes *delta_ptr >= 0, and + * *recv_window_size_ptr is not decreased by *delta_ptr. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_FLOW_CONTROL + * local_window_size overflow or gets negative. + */ +int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr); + +/* + * Returns non-zero if the function decided that WINDOW_UPDATE should + * be sent. + */ +int nghttp2_should_send_window_update(int32_t local_window_size, + int32_t recv_window_size); + +/* + * Copies the buffer |src| of length |len| to the destination pointed + * by the |dest|, assuming that the |dest| is at lest |len| bytes long + * . Returns dest + len. + */ +uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len); + +#endif /* NGHTTP2_HELPER_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_http.c b/Utilities/cmnghttp2/lib/nghttp2_http.c new file mode 100644 index 00000000000..62f57b6aec7 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_http.c @@ -0,0 +1,530 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_http.h" + +#include +#include +#include + +#include "nghttp2_hd.h" +#include "nghttp2_helper.h" + +static uint8_t downcase(uint8_t c) { + return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; +} + +static int memieq(const void *a, const void *b, size_t n) { + size_t i; + const uint8_t *aa = a, *bb = b; + + for (i = 0; i < n; ++i) { + if (downcase(aa[i]) != downcase(bb[i])) { + return 0; + } + } + return 1; +} + +#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) + +static int64_t parse_uint(const uint8_t *s, size_t len) { + int64_t n = 0; + size_t i; + if (len == 0) { + return -1; + } + for (i = 0; i < len; ++i) { + if ('0' <= s[i] && s[i] <= '9') { + if (n > INT64_MAX / 10) { + return -1; + } + n *= 10; + if (n > INT64_MAX - (s[i] - '0')) { + return -1; + } + n += s[i] - '0'; + continue; + } + return -1; + } + return n; +} + +static int lws(const uint8_t *s, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + if (s[i] != ' ' && s[i] != '\t') { + return 0; + } + } + return 1; +} + +static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv, + int flag) { + if (stream->http_flags & flag) { + return 0; + } + if (lws(nv->value->base, nv->value->len)) { + return 0; + } + stream->http_flags = (uint16_t)(stream->http_flags | flag); + return 1; +} + +static int expect_response_body(nghttp2_stream *stream) { + return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 && + stream->status_code / 100 != 1 && stream->status_code != 304 && + stream->status_code != 204; +} + +/* For "http" or "https" URIs, OPTIONS request may have "*" in :path + header field to represent system-wide OPTIONS request. Otherwise, + :path header field value must start with "/". This function must + be called after ":method" header field was received. This function + returns nonzero if path is valid.*/ +static int check_path(nghttp2_stream *stream) { + return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 || + ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) || + ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) && + (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK))); +} + +static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, + int trailer, int connect_protocol) { + if (nv->name->base[0] == ':') { + if (trailer || + (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + switch (nv->token) { + case NGHTTP2_TOKEN__AUTHORITY: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + case NGHTTP2_TOKEN__METHOD: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + switch (nv->value->len) { + case 4: + if (lstreq("HEAD", nv->value->base, nv->value->len)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + } + break; + case 7: + switch (nv->value->base[6]) { + case 'T': + if (lstreq("CONNECT", nv->value->base, nv->value->len)) { + if (stream->stream_id % 2 == 0) { + /* we won't allow CONNECT for push */ + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; + } + break; + case 'S': + if (lstreq("OPTIONS", nv->value->base, nv->value->len)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS; + } + break; + } + break; + } + break; + case NGHTTP2_TOKEN__PATH: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (nv->value->base[0] == '/') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR; + } else if (nv->value->len == 1 && nv->value->base[0] == '*') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK; + } + break; + case NGHTTP2_TOKEN__SCHEME: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) || + (nv->value->len == 5 && memieq("https", nv->value->base, 5))) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP; + } + break; + case NGHTTP2_TOKEN__PROTOCOL: + if (!connect_protocol) { + return NGHTTP2_ERR_HTTP_HEADER; + } + + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + case NGHTTP2_TOKEN_HOST: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + case NGHTTP2_TOKEN_CONTENT_LENGTH: { + if (stream->content_length != -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = parse_uint(nv->value->base, nv->value->len); + if (stream->content_length == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP2_TOKEN_CONNECTION: + case NGHTTP2_TOKEN_KEEP_ALIVE: + case NGHTTP2_TOKEN_PROXY_CONNECTION: + case NGHTTP2_TOKEN_TRANSFER_ENCODING: + case NGHTTP2_TOKEN_UPGRADE: + return NGHTTP2_ERR_HTTP_HEADER; + case NGHTTP2_TOKEN_TE: + if (!lstrieq("trailers", nv->value->base, nv->value->len)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + default: + if (nv->name->base[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + if (nv->name->base[0] != ':') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, + int trailer) { + if (nv->name->base[0] == ':') { + if (trailer || + (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + switch (nv->token) { + case NGHTTP2_TOKEN__STATUS: { + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (nv->value->len != 3) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len); + if (stream->status_code == -1 || stream->status_code == 101) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + case NGHTTP2_TOKEN_CONTENT_LENGTH: { + if (stream->status_code == 204) { + /* content-length header field in 204 response is prohibited by + RFC 7230. But some widely used servers send content-length: + 0. Until they get fixed, we ignore it. */ + if (stream->content_length != -1) { + /* Found multiple content-length field */ + return NGHTTP2_ERR_HTTP_HEADER; + } + if (!lstrieq("0", nv->value->base, nv->value->len)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = 0; + return NGHTTP2_ERR_REMOVE_HTTP_HEADER; + } + if (stream->status_code / 100 == 1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */ + if (stream->status_code / 100 == 2 && + (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) { + return NGHTTP2_ERR_REMOVE_HTTP_HEADER; + } + if (stream->content_length != -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = parse_uint(nv->value->base, nv->value->len); + if (stream->content_length == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP2_TOKEN_CONNECTION: + case NGHTTP2_TOKEN_KEEP_ALIVE: + case NGHTTP2_TOKEN_PROXY_CONNECTION: + case NGHTTP2_TOKEN_TRANSFER_ENCODING: + case NGHTTP2_TOKEN_UPGRADE: + return NGHTTP2_ERR_HTTP_HEADER; + case NGHTTP2_TOKEN_TE: + if (!lstrieq("trailers", nv->value->base, nv->value->len)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + default: + if (nv->name->base[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + if (nv->name->base[0] != ':') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +static int check_scheme(const uint8_t *value, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + + if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) { + return 0; + } + + last = value + len; + ++value; + + for (; value != last; ++value) { + if (!(('A' <= *value && *value <= 'Z') || + ('a' <= *value && *value <= 'z') || + ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' || + *value == '.')) { + return 0; + } + } + return 1; +} + +int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + nghttp2_frame *frame, nghttp2_hd_nv *nv, + int trailer) { + int rv; + + /* We are strict for pseudo header field. One bad character should + lead to fail. OTOH, we should be a bit forgiving for regular + headers, since existing public internet has so much illegal + headers floating around and if we kill the stream because of + this, we may disrupt many web sites and/or libraries. So we + become conservative here, and just ignore those illegal regular + headers. */ + if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) { + size_t i; + if (nv->name->len > 0 && nv->name->base[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + /* header field name must be lower-cased without exception */ + for (i = 0; i < nv->name->len; ++i) { + uint8_t c = nv->name->base[i]; + if ('A' <= c && c <= 'Z') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + /* When ignoring regular headers, we set this flag so that we + still enforce header field ordering rule for pseudo header + fields. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP2_ERR_IGN_HTTP_HEADER; + } + + if (nv->token == NGHTTP2_TOKEN__AUTHORITY || + nv->token == NGHTTP2_TOKEN_HOST) { + rv = nghttp2_check_authority(nv->value->base, nv->value->len); + } else if (nv->token == NGHTTP2_TOKEN__SCHEME) { + rv = check_scheme(nv->value->base, nv->value->len); + } else { + rv = nghttp2_check_header_value(nv->value->base, nv->value->len); + } + + if (rv == 0) { + assert(nv->name->len > 0); + if (nv->name->base[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + /* When ignoring regular headers, we set this flag so that we + still enforce header field ordering rule for pseudo header + fields. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP2_ERR_IGN_HTTP_HEADER; + } + + if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { + return http_request_on_header(stream, nv, trailer, + session->server && + session->pending_enable_connect_protocol); + } + + return http_response_on_header(stream, nv, trailer); +} + +int nghttp2_http_on_request_headers(nghttp2_stream *stream, + nghttp2_frame *frame) { + if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) && + (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) { + if ((stream->http_flags & + (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) || + (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) { + return -1; + } + stream->content_length = -1; + } else { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) != + NGHTTP2_HTTP_FLAG_REQ_HEADERS || + (stream->http_flags & + (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) { + return -1; + } + if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) && + ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 || + (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) { + return -1; + } + if (!check_path(stream)) { + return -1; + } + } + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + /* we are going to reuse data fields for upcoming response. Clear + them now, except for method flags. */ + stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL; + stream->content_length = -1; + } + + return 0; +} + +int nghttp2_http_on_response_headers(nghttp2_stream *stream) { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) { + return -1; + } + + if (stream->status_code / 100 == 1) { + /* non-final response */ + stream->http_flags = + (uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) | + NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + stream->content_length = -1; + stream->status_code = -1; + return 0; + } + + stream->http_flags = + (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + + if (!expect_response_body(stream)) { + stream->content_length = 0; + } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT | + NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) { + stream->content_length = -1; + } + + return 0; +} + +int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, + nghttp2_frame *frame) { + (void)stream; + + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return -1; + } + + return 0; +} + +int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) { + if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + return -1; + } + + if (stream->content_length != -1 && + stream->content_length != stream->recv_content_length) { + return -1; + } + + return 0; +} + +int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) { + stream->recv_content_length += (int64_t)n; + + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || + (stream->content_length != -1 && + stream->recv_content_length > stream->content_length)) { + return -1; + } + + return 0; +} + +void nghttp2_http_record_request_method(nghttp2_stream *stream, + nghttp2_frame *frame) { + const nghttp2_nv *nva; + size_t nvlen; + size_t i; + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + nva = frame->headers.nva; + nvlen = frame->headers.nvlen; + break; + case NGHTTP2_PUSH_PROMISE: + nva = frame->push_promise.nva; + nvlen = frame->push_promise.nvlen; + break; + default: + return; + } + + /* TODO we should do this strictly. */ + for (i = 0; i < nvlen; ++i) { + const nghttp2_nv *nv = &nva[i]; + if (!(nv->namelen == 7 && nv->name[6] == 'd' && + memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { + continue; + } + if (lstreq("CONNECT", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; + return; + } + if (lstreq("HEAD", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + return; + } + return; + } +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_http.h b/Utilities/cmnghttp2/lib/nghttp2_http.h new file mode 100644 index 00000000000..dd057cdb607 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_http.h @@ -0,0 +1,97 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HTTP_H +#define NGHTTP2_HTTP_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_session.h" +#include "nghttp2_stream.h" + +/* + * This function is called when HTTP header field |nv| in |frame| is + * received for |stream|. This function will validate |nv| against + * the current state of stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_HTTP_HEADER + * Invalid HTTP header field was received. + * NGHTTP2_ERR_IGN_HTTP_HEADER + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ +int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + nghttp2_frame *frame, nghttp2_hd_nv *nv, + int trailer); + +/* + * This function is called when request header is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_request_headers(nghttp2_stream *stream, + nghttp2_frame *frame); + +/* + * This function is called when response header is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_response_headers(nghttp2_stream *stream); + +/* + * This function is called trailer header (for both request and + * response) is received. This function performs validation and + * returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, + nghttp2_frame *frame); + +/* + * This function is called when END_STREAM flag is seen in incoming + * frame. This function performs validation and returns 0 if it + * succeeds, or -1. + */ +int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream); + +/* + * This function is called when chunk of data is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); + +/* + * This function inspects header field in |frame| and records its + * method in stream->http_flags. If frame->hd.type is neither + * NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does + * nothing. + */ +void nghttp2_http_record_request_method(nghttp2_stream *stream, + nghttp2_frame *frame); + +#endif /* NGHTTP2_HTTP_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_int.h b/Utilities/cmnghttp2/lib/nghttp2_int.h new file mode 100644 index 00000000000..b23585ccb27 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_int.h @@ -0,0 +1,58 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_INT_H +#define NGHTTP2_INT_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* Macros, types and constants for internal use */ + +/* "less" function, return nonzero if |lhs| is less than |rhs|. */ +typedef int (*nghttp2_less)(const void *lhs, const void *rhs); + +/* Internal error code. They must be in the range [-499, -100], + inclusive. */ +typedef enum { + NGHTTP2_ERR_CREDENTIAL_PENDING = -101, + NGHTTP2_ERR_IGN_HEADER_BLOCK = -103, + NGHTTP2_ERR_IGN_PAYLOAD = -104, + /* + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ + NGHTTP2_ERR_IGN_HTTP_HEADER = -105, + /* + * Invalid HTTP header field was received, and it is ignored. + * Unlike NGHTTP2_ERR_IGN_HTTP_HEADER, this does not invoke + * nghttp2_on_invalid_header_callback. + */ + NGHTTP2_ERR_REMOVE_HTTP_HEADER = -106 +} nghttp2_internal_error; + +#endif /* NGHTTP2_INT_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_map.c b/Utilities/cmnghttp2/lib/nghttp2_map.c new file mode 100644 index 00000000000..4d9f97b47e2 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_map.c @@ -0,0 +1,189 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_map.h" + +#include + +#define INITIAL_TABLE_LENGTH 256 + +int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { + map->mem = mem; + map->tablelen = INITIAL_TABLE_LENGTH; + map->table = + nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *)); + if (map->table == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void nghttp2_map_free(nghttp2_map *map) { + nghttp2_mem_free(map->mem, map->table); +} + +void nghttp2_map_each_free(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr) { + uint32_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp2_map_entry *next = entry->next; + func(entry, ptr); + entry = next; + } + map->table[i] = NULL; + } +} + +int nghttp2_map_each(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr) { + int rv; + uint32_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp2_map_entry *entry; + for (entry = map->table[i]; entry; entry = entry->next) { + rv = func(entry, ptr); + if (rv != 0) { + return rv; + } + } + } + return 0; +} + +void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) { + entry->key = key; + entry->next = NULL; +} + +/* Same hash function in android HashMap source code. */ +/* The |mod| must be power of 2 */ +static uint32_t hash(int32_t key, uint32_t mod) { + uint32_t h = (uint32_t)key; + h ^= (h >> 20) ^ (h >> 12); + h ^= (h >> 7) ^ (h >> 4); + return h & (mod - 1); +} + +static int insert(nghttp2_map_entry **table, uint32_t tablelen, + nghttp2_map_entry *entry) { + uint32_t h = hash(entry->key, tablelen); + if (table[h] == NULL) { + table[h] = entry; + } else { + nghttp2_map_entry *p; + /* We won't allow duplicated key, so check it out. */ + for (p = table[h]; p; p = p->next) { + if (p->key == entry->key) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } + entry->next = table[h]; + table[h] = entry; + } + return 0; +} + +/* new_tablelen must be power of 2 */ +static int resize(nghttp2_map *map, uint32_t new_tablelen) { + uint32_t i; + nghttp2_map_entry **new_table; + + new_table = + nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *)); + if (new_table == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { + nghttp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp2_map_entry *next = entry->next; + entry->next = NULL; + /* This function must succeed */ + insert(new_table, new_tablelen, entry); + entry = next; + } + } + nghttp2_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; + map->table = new_table; + + return 0; +} + +int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) { + int rv; + /* Load factor is 0.75 */ + if ((map->size + 1) * 4 > map->tablelen * 3) { + rv = resize(map, map->tablelen * 2); + if (rv != 0) { + return rv; + } + } + rv = insert(map->table, map->tablelen, new_entry); + if (rv != 0) { + return rv; + } + ++map->size; + return 0; +} + +nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) { + uint32_t h; + nghttp2_map_entry *entry; + h = hash(key, map->tablelen); + for (entry = map->table[h]; entry; entry = entry->next) { + if (entry->key == key) { + return entry; + } + } + return NULL; +} + +int nghttp2_map_remove(nghttp2_map *map, key_type key) { + uint32_t h; + nghttp2_map_entry **dst; + + h = hash(key, map->tablelen); + + for (dst = &map->table[h]; *dst; dst = &(*dst)->next) { + if ((*dst)->key != key) { + continue; + } + + *dst = (*dst)->next; + --map->size; + return 0; + } + return NGHTTP2_ERR_INVALID_ARGUMENT; +} + +size_t nghttp2_map_size(nghttp2_map *map) { return map->size; } diff --git a/Utilities/cmnghttp2/lib/nghttp2_map.h b/Utilities/cmnghttp2/lib/nghttp2_map.h new file mode 100644 index 00000000000..f6e29e35f2d --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_map.h @@ -0,0 +1,144 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_MAP_H +#define NGHTTP2_MAP_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +/* Implementation of unordered map */ + +typedef int32_t key_type; + +typedef struct nghttp2_map_entry { + struct nghttp2_map_entry *next; + key_type key; +#if SIZEOF_INT_P == 4 + /* we requires 8 bytes aligment */ + int64_t pad; +#endif +} nghttp2_map_entry; + +typedef struct { + nghttp2_map_entry **table; + nghttp2_mem *mem; + size_t size; + uint32_t tablelen; +} nghttp2_map; + +/* + * Initializes the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |map|. The stored entries + * are not freed by this function. Use nghttp2_map_each_free() to free + * each entries. + */ +void nghttp2_map_free(nghttp2_map *map); + +/* + * Deallocates each entries using |func| function and any resources + * allocated for |map|. The |func| function is responsible for freeing + * given the |entry| object. The |ptr| will be passed to the |func| as + * send argument. The return value of the |func| will be ignored. + */ +void nghttp2_map_each_free(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr); + +/* + * Initializes the |entry| with the |key|. All entries to be inserted + * to the map must be initialized with this function. + */ +void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key); + +/* + * Inserts the new |entry| with the key |entry->key| to the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The item associated by |key| already exists. + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry); + +/* + * Returns the entry associated by the key |key|. If there is no such + * entry, this function returns NULL. + */ +nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key); + +/* + * Removes the entry associated by the key |key| from the |map|. The + * removed entry is not freed by this function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The entry associated by |key| does not exist. + */ +int nghttp2_map_remove(nghttp2_map *map, key_type key); + +/* + * Returns the number of items stored in the map |map|. + */ +size_t nghttp2_map_size(nghttp2_map *map); + +/* + * Applies the function |func| to each entry in the |map| with the + * optional user supplied pointer |ptr|. + * + * If the |func| returns 0, this function calls the |func| with the + * next entry. If the |func| returns nonzero, it will not call the + * |func| for further entries and return the return value of the + * |func| immediately. Thus, this function returns 0 if all the + * invocations of the |func| return 0, or nonzero value which the last + * invocation of |func| returns. + * + * Don't use this function to free each entry. Use + * nghttp2_map_each_free() instead. + */ +int nghttp2_map_each(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr); + +#endif /* NGHTTP2_MAP_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_mem.c b/Utilities/cmnghttp2/lib/nghttp2_mem.c new file mode 100644 index 00000000000..6a449cffd70 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_mem.c @@ -0,0 +1,74 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_mem.h" + +static void *default_malloc(size_t size, void *mem_user_data) { + (void)mem_user_data; + + return malloc(size); +} + +static void default_free(void *ptr, void *mem_user_data) { + (void)mem_user_data; + + free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return realloc(ptr, size); +} + +static nghttp2_mem mem_default = {NULL, default_malloc, default_free, + default_calloc, default_realloc}; + +nghttp2_mem *nghttp2_mem_default(void) { return &mem_default; } + +void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) { + return mem->malloc(size, mem->mem_user_data); +} + +void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) { + mem->free(ptr, mem->mem_user_data); +} + +void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data) { + free_func(ptr, mem_user_data); +} + +void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) { + return mem->calloc(nmemb, size, mem->mem_user_data); +} + +void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) { + return mem->realloc(ptr, size, mem->mem_user_data); +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_mem.h b/Utilities/cmnghttp2/lib/nghttp2_mem.h new file mode 100644 index 00000000000..f83dbcb8f9a --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_mem.h @@ -0,0 +1,45 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_MEM_H +#define NGHTTP2_MEM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* The default, system standard memory allocator */ +nghttp2_mem *nghttp2_mem_default(void); + +/* Convenient wrapper functions to call allocator function in + |mem|. */ +void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size); +void nghttp2_mem_free(nghttp2_mem *mem, void *ptr); +void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data); +void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size); +void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size); + +#endif /* NGHTTP2_MEM_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_net.h b/Utilities/cmnghttp2/lib/nghttp2_net.h new file mode 100644 index 00000000000..95ffee74a14 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_net.h @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_NET_H +#define NGHTTP2_NET_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#include + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +# ifdef _MSC_VER +# define STIN static __inline +# else +# define STIN static inline +# endif + +STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#endif /* WIN32 */ + +#endif /* NGHTTP2_NET_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_npn.c b/Utilities/cmnghttp2/lib/nghttp2_npn.c new file mode 100644 index 00000000000..d1384c80758 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_npn.c @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_npn.h" + +#include + +static int select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + const char *key, unsigned int keylen) { + unsigned int i; + for (i = 0; i + keylen <= inlen; i += (unsigned int)(in[i] + 1)) { + if (memcmp(&in[i], key, keylen) == 0) { + *out = (unsigned char *)&in[i + 1]; + *outlen = in[i]; + return 0; + } + } + return -1; +} + +#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1" +#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1) + +int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen) { + if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, + NGHTTP2_PROTO_ALPN_LEN) == 0) { + return 1; + } + if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN, + NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) { + return 0; + } + return -1; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_npn.h b/Utilities/cmnghttp2/lib/nghttp2_npn.h new file mode 100644 index 00000000000..c6f1c04b683 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_npn.h @@ -0,0 +1,34 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_NPN_H +#define NGHTTP2_NPN_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP2_NPN_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_option.c b/Utilities/cmnghttp2/lib/nghttp2_option.c new file mode 100644 index 00000000000..e53f22d367f --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_option.c @@ -0,0 +1,123 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_option.h" + +#include "nghttp2_session.h" + +int nghttp2_option_new(nghttp2_option **option_ptr) { + *option_ptr = calloc(1, sizeof(nghttp2_option)); + + if (*option_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +void nghttp2_option_del(nghttp2_option *option) { free(option); } + +void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE; + option->no_auto_window_update = val; +} + +void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val) { + option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS; + option->peer_max_concurrent_streams = val; +} + +void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC; + option->no_recv_client_magic = val; +} + +void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING; + option->no_http_messaging = val; +} + +void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, + uint32_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS; + option->max_reserved_remote_streams = val; +} + +static void set_ext_type(uint8_t *ext_types, uint8_t type) { + ext_types[type / 8] = (uint8_t)(ext_types[type / 8] | (1 << (type & 0x7))); +} + +void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, + uint8_t type) { + if (type < 10) { + return; + } + + option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES; + set_ext_type(option->user_recv_ext_types, type); +} + +void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, + uint8_t type) { + switch (type) { + case NGHTTP2_ALTSVC: + option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; + option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC; + return; + case NGHTTP2_ORIGIN: + option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; + option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN; + return; + default: + return; + } +} + +void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_PING_ACK; + option->no_auto_ping_ack = val; +} + +void nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, + size_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH; + option->max_send_header_block_length = val; +} + +void nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, + size_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE; + option->max_deflate_dynamic_table_size = val; +} + +void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_CLOSED_STREAMS; + option->no_closed_streams = val; +} + +void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK; + option->max_outbound_ack = val; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_option.h b/Utilities/cmnghttp2/lib/nghttp2_option.h new file mode 100644 index 00000000000..1f740aaa6e3 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_option.h @@ -0,0 +1,131 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_OPTION_H +#define NGHTTP2_OPTION_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/** + * Configuration options + */ +typedef enum { + /** + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * nghttp2_session_consume() to indicate the amount of consumed + * DATA. By default, this option is set to zero. + */ + NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1, + /** + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without + * specifying this option, before the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote + * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may + * cause problem if local endpoint submits lots of requests + * initially and sending them at once to the remote peer may lead to + * the rejection of some requests. Specifying this option to the + * sensible value, say 100, may avoid this kind of issue. This value + * will be overwritten if the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. + */ + NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1, + NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, + NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3, + NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4, + NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5, + NGHTTP2_OPT_NO_AUTO_PING_ACK = 1 << 6, + NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES = 1 << 7, + NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH = 1 << 8, + NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9, + NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10, + NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11, +} nghttp2_option_flag; + +/** + * Struct to store option values for nghttp2_session. + */ +struct nghttp2_option { + /** + * NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH + */ + size_t max_send_header_block_length; + /** + * NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE + */ + size_t max_deflate_dynamic_table_size; + /** + * NGHTTP2_OPT_MAX_OUTBOUND_ACK + */ + size_t max_outbound_ack; + /** + * Bitwise OR of nghttp2_option_flag to determine that which fields + * are specified. + */ + uint32_t opt_set_mask; + /** + * NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS + */ + uint32_t peer_max_concurrent_streams; + /** + * NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS + */ + uint32_t max_reserved_remote_streams; + /** + * NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES + */ + uint32_t builtin_recv_ext_types; + /** + * NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE + */ + int no_auto_window_update; + /** + * NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC + */ + int no_recv_client_magic; + /** + * NGHTTP2_OPT_NO_HTTP_MESSAGING + */ + int no_http_messaging; + /** + * NGHTTP2_OPT_NO_AUTO_PING_ACK + */ + int no_auto_ping_ack; + /** + * NGHTTP2_OPT_NO_CLOSED_STREAMS + */ + int no_closed_streams; + /** + * NGHTTP2_OPT_USER_RECV_EXT_TYPES + */ + uint8_t user_recv_ext_types[32]; +}; + +#endif /* NGHTTP2_OPTION_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_outbound_item.c b/Utilities/cmnghttp2/lib/nghttp2_outbound_item.c new file mode 100644 index 00000000000..f651c8029ac --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_outbound_item.c @@ -0,0 +1,127 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_outbound_item.h" + +#include +#include + +void nghttp2_outbound_item_init(nghttp2_outbound_item *item) { + item->cycle = 0; + item->qnext = NULL; + item->queued = 0; + + memset(&item->aux_data, 0, sizeof(nghttp2_aux_data)); +} + +void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) { + nghttp2_frame *frame; + + if (item == NULL) { + return; + } + + frame = &item->frame; + + switch (frame->hd.type) { + case NGHTTP2_DATA: + nghttp2_frame_data_free(&frame->data); + break; + case NGHTTP2_HEADERS: + nghttp2_frame_headers_free(&frame->headers, mem); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_priority_free(&frame->priority); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_rst_stream_free(&frame->rst_stream); + break; + case NGHTTP2_SETTINGS: + nghttp2_frame_settings_free(&frame->settings, mem); + break; + case NGHTTP2_PUSH_PROMISE: + nghttp2_frame_push_promise_free(&frame->push_promise, mem); + break; + case NGHTTP2_PING: + nghttp2_frame_ping_free(&frame->ping); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_goaway_free(&frame->goaway, mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_window_update_free(&frame->window_update); + break; + default: { + nghttp2_ext_aux_data *aux_data; + + aux_data = &item->aux_data.ext; + + if (aux_data->builtin == 0) { + nghttp2_frame_extension_free(&frame->ext); + break; + } + + switch (frame->hd.type) { + case NGHTTP2_ALTSVC: + nghttp2_frame_altsvc_free(&frame->ext, mem); + break; + case NGHTTP2_ORIGIN: + nghttp2_frame_origin_free(&frame->ext, mem); + break; + default: + assert(0); + break; + } + } + } +} + +void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) { + q->head = q->tail = NULL; + q->n = 0; +} + +void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, + nghttp2_outbound_item *item) { + if (q->tail) { + q->tail = q->tail->qnext = item; + } else { + q->head = q->tail = item; + } + ++q->n; +} + +void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) { + nghttp2_outbound_item *item; + if (!q->head) { + return; + } + item = q->head; + q->head = q->head->qnext; + item->qnext = NULL; + if (!q->head) { + q->tail = NULL; + } + --q->n; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_outbound_item.h b/Utilities/cmnghttp2/lib/nghttp2_outbound_item.h new file mode 100644 index 00000000000..b5f503a312d --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_outbound_item.h @@ -0,0 +1,166 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_OUTBOUND_ITEM_H +#define NGHTTP2_OUTBOUND_ITEM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_frame.h" +#include "nghttp2_mem.h" + +/* struct used for HEADERS and PUSH_PROMISE frame */ +typedef struct { + nghttp2_data_provider data_prd; + void *stream_user_data; + /* error code when request HEADERS is canceled by RST_STREAM while + it is in queue. */ + uint32_t error_code; + /* nonzero if request HEADERS is canceled. The error code is stored + in |error_code|. */ + uint8_t canceled; +} nghttp2_headers_aux_data; + +/* struct used for DATA frame */ +typedef struct { + /** + * The data to be sent for this DATA frame. + */ + nghttp2_data_provider data_prd; + /** + * The flags of DATA frame. We use separate flags here and + * nghttp2_data frame. The latter contains flags actually sent to + * peer. This |flags| may contain NGHTTP2_FLAG_END_STREAM and only + * when |eof| becomes nonzero, flags in nghttp2_data has + * NGHTTP2_FLAG_END_STREAM set. + */ + uint8_t flags; + /** + * The flag to indicate whether EOF was reached or not. Initially + * |eof| is 0. It becomes 1 after all data were read. + */ + uint8_t eof; + /** + * The flag to indicate that NGHTTP2_DATA_FLAG_NO_COPY is used. + */ + uint8_t no_copy; +} nghttp2_data_aux_data; + +typedef enum { + NGHTTP2_GOAWAY_AUX_NONE = 0x0, + /* indicates that session should be terminated after the + transmission of this frame. */ + NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1, + /* indicates that this GOAWAY is just a notification for graceful + shutdown. No nghttp2_session.goaway_flags should be updated on + the reaction to this frame. */ + NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2 +} nghttp2_goaway_aux_flag; + +/* struct used for GOAWAY frame */ +typedef struct { + /* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */ + uint8_t flags; +} nghttp2_goaway_aux_data; + +/* struct used for extension frame */ +typedef struct { + /* nonzero if this extension frame is serialized by library + function, instead of user-defined callbacks. */ + uint8_t builtin; +} nghttp2_ext_aux_data; + +/* Additional data which cannot be stored in nghttp2_frame struct */ +typedef union { + nghttp2_data_aux_data data; + nghttp2_headers_aux_data headers; + nghttp2_goaway_aux_data goaway; + nghttp2_ext_aux_data ext; +} nghttp2_aux_data; + +struct nghttp2_outbound_item; +typedef struct nghttp2_outbound_item nghttp2_outbound_item; + +struct nghttp2_outbound_item { + nghttp2_frame frame; + /* Storage for extension frame payload. frame->ext.payload points + to this structure to avoid frequent memory allocation. */ + nghttp2_ext_frame_payload ext_frame_payload; + nghttp2_aux_data aux_data; + /* The priority used in priority comparion. Smaller is served + earlier. For PING, SETTINGS and non-DATA frames (excluding + response HEADERS frame) have dedicated cycle value defined above. + For DATA frame, cycle is computed by taking into account of + effective weight and frame payload length previously sent, so + that the amount of transmission is distributed across streams + proportional to effective weight (inside a tree). */ + uint64_t cycle; + nghttp2_outbound_item *qnext; + /* nonzero if this object is queued, except for DATA or HEADERS + which are attached to stream as item. */ + uint8_t queued; +}; + +/* + * Initializes |item|. No memory allocation is done in this function. + * Don't call nghttp2_outbound_item_free() until frame member is + * initialized. + */ +void nghttp2_outbound_item_init(nghttp2_outbound_item *item); + +/* + * Deallocates resource for |item|. If |item| is NULL, this function + * does nothing. + */ +void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem); + +/* + * queue for nghttp2_outbound_item. + */ +typedef struct { + nghttp2_outbound_item *head, *tail; + /* number of items in this queue. */ + size_t n; +} nghttp2_outbound_queue; + +void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q); + +/* Pushes |item| into |q| */ +void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, + nghttp2_outbound_item *item); + +/* Pops |item| at the top from |q|. If |q| is empty, nothing + happens. */ +void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q); + +/* Returns the top item. */ +#define nghttp2_outbound_queue_top(Q) ((Q)->head) + +/* Returns the size of the queue */ +#define nghttp2_outbound_queue_size(Q) ((Q)->n) + +#endif /* NGHTTP2_OUTBOUND_ITEM_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_pq.c b/Utilities/cmnghttp2/lib/nghttp2_pq.c new file mode 100644 index 00000000000..bebccc76064 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_pq.c @@ -0,0 +1,184 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_pq.h" + +#include +#include + +#include "nghttp2_helper.h" + +int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) { + pq->mem = mem; + pq->capacity = 0; + pq->q = NULL; + pq->length = 0; + pq->less = less; + return 0; +} + +void nghttp2_pq_free(nghttp2_pq *pq) { + nghttp2_mem_free(pq->mem, pq->q); + pq->q = NULL; +} + +static void swap(nghttp2_pq *pq, size_t i, size_t j) { + nghttp2_pq_entry *a = pq->q[i]; + nghttp2_pq_entry *b = pq->q[j]; + + pq->q[i] = b; + b->index = i; + pq->q[j] = a; + a->index = j; +} + +static void bubble_up(nghttp2_pq *pq, size_t index) { + size_t parent; + while (index != 0) { + parent = (index - 1) / 2; + if (!pq->less(pq->q[index], pq->q[parent])) { + return; + } + swap(pq, parent, index); + index = parent; + } +} + +int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item) { + if (pq->capacity <= pq->length) { + void *nq; + size_t ncapacity; + + ncapacity = nghttp2_max(4, (pq->capacity * 2)); + + nq = nghttp2_mem_realloc(pq->mem, pq->q, + ncapacity * sizeof(nghttp2_pq_entry *)); + if (nq == NULL) { + return NGHTTP2_ERR_NOMEM; + } + pq->capacity = ncapacity; + pq->q = nq; + } + pq->q[pq->length] = item; + item->index = pq->length; + ++pq->length; + bubble_up(pq, pq->length - 1); + return 0; +} + +nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq) { + if (pq->length == 0) { + return NULL; + } else { + return pq->q[0]; + } +} + +static void bubble_down(nghttp2_pq *pq, size_t index) { + size_t i, j, minindex; + for (;;) { + j = index * 2 + 1; + minindex = index; + for (i = 0; i < 2; ++i, ++j) { + if (j >= pq->length) { + break; + } + if (pq->less(pq->q[j], pq->q[minindex])) { + minindex = j; + } + } + if (minindex == index) { + return; + } + swap(pq, index, minindex); + index = minindex; + } +} + +void nghttp2_pq_pop(nghttp2_pq *pq) { + if (pq->length > 0) { + pq->q[0] = pq->q[pq->length - 1]; + pq->q[0]->index = 0; + --pq->length; + bubble_down(pq, 0); + } +} + +void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item) { + assert(pq->q[item->index] == item); + + if (item->index == 0) { + nghttp2_pq_pop(pq); + return; + } + + if (item->index == pq->length - 1) { + --pq->length; + return; + } + + pq->q[item->index] = pq->q[pq->length - 1]; + pq->q[item->index]->index = item->index; + --pq->length; + + if (pq->less(item, pq->q[item->index])) { + bubble_down(pq, item->index); + } else { + bubble_up(pq, item->index); + } +} + +int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; } + +size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; } + +void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { + size_t i; + int rv = 0; + if (pq->length == 0) { + return; + } + for (i = 0; i < pq->length; ++i) { + rv |= (*fun)(pq->q[i], arg); + } + if (rv) { + for (i = pq->length; i > 0; --i) { + bubble_down(pq, i - 1); + } + } +} + +int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { + size_t i; + + if (pq->length == 0) { + return 0; + } + for (i = 0; i < pq->length; ++i) { + if ((*fun)(pq->q[i], arg)) { + return 1; + } + } + return 0; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_pq.h b/Utilities/cmnghttp2/lib/nghttp2_pq.h new file mode 100644 index 00000000000..2d7b702ac18 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_pq.h @@ -0,0 +1,130 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_PQ_H +#define NGHTTP2_PQ_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +/* Implementation of priority queue */ + +typedef struct { + size_t index; +} nghttp2_pq_entry; + +typedef struct { + /* The pointer to the pointer to the item stored */ + nghttp2_pq_entry **q; + /* Memory allocator */ + nghttp2_mem *mem; + /* The number of items stored */ + size_t length; + /* The maximum number of items this pq can store. This is + automatically extended when length is reached to this value. */ + size_t capacity; + /* The less function between items */ + nghttp2_less less; +} nghttp2_pq; + +/* + * Initializes priority queue |pq| with compare function |cmp|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |pq|. The stored items are + * not freed by this function. + */ +void nghttp2_pq_free(nghttp2_pq *pq); + +/* + * Adds |item| to the priority queue |pq|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item); + +/* + * Returns item at the top of the queue |pq|. If the queue is empty, + * this function returns NULL. + */ +nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq); + +/* + * Pops item at the top of the queue |pq|. The popped item is not + * freed by this function. + */ +void nghttp2_pq_pop(nghttp2_pq *pq); + +/* + * Returns nonzero if the queue |pq| is empty. + */ +int nghttp2_pq_empty(nghttp2_pq *pq); + +/* + * Returns the number of items in the queue |pq|. + */ +size_t nghttp2_pq_size(nghttp2_pq *pq); + +typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg); + +/* + * Updates each item in |pq| using function |fun| and re-construct + * priority queue. The |fun| must return non-zero if it modifies the + * item in a way that it affects ordering in the priority queue. The + * |arg| is passed to the 2nd parameter of |fun|. + */ +void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); + +/* + * Applys |fun| to each item in |pq|. The |arg| is passed as arg + * parameter to callback function. This function must not change the + * ordering key. If the return value from callback is nonzero, this + * function returns 1 immediately without iterating remaining items. + * Otherwise this function returns 0. + */ +int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); + +/* + * Removes |item| from priority queue. + */ +void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item); + +#endif /* NGHTTP2_PQ_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_priority_spec.c b/Utilities/cmnghttp2/lib/nghttp2_priority_spec.c new file mode 100644 index 00000000000..c2196e30630 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_priority_spec.c @@ -0,0 +1,52 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_priority_spec.h" + +void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, int32_t weight, + int exclusive) { + pri_spec->stream_id = stream_id; + pri_spec->weight = weight; + pri_spec->exclusive = exclusive != 0; +} + +void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) { + pri_spec->stream_id = 0; + pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT; + pri_spec->exclusive = 0; +} + +int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) { + return pri_spec->stream_id == 0 && + pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0; +} + +void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec) { + if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) { + pri_spec->weight = NGHTTP2_MIN_WEIGHT; + } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) { + pri_spec->weight = NGHTTP2_MAX_WEIGHT; + } +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_priority_spec.h b/Utilities/cmnghttp2/lib/nghttp2_priority_spec.h new file mode 100644 index 00000000000..92ece822a8f --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_priority_spec.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_PRIORITY_SPEC_H +#define NGHTTP2_PRIORITY_SPEC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * This function normalizes pri_spec->weight if it is out of range. + * If pri_spec->weight is less than NGHTTP2_MIN_WEIGHT, it is set to + * NGHTTP2_MIN_WEIGHT. If pri_spec->weight is larger than + * NGHTTP2_MAX_WEIGHT, it is set to NGHTTP2_MAX_WEIGHT. + */ +void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec); + +#endif /* NGHTTP2_PRIORITY_SPEC_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_queue.c b/Utilities/cmnghttp2/lib/nghttp2_queue.c new file mode 100644 index 00000000000..055eb69c7e5 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_queue.c @@ -0,0 +1,85 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_queue.h" + +#include +#include + +void nghttp2_queue_init(nghttp2_queue *queue) { + queue->front = queue->back = NULL; +} + +void nghttp2_queue_free(nghttp2_queue *queue) { + if (!queue) { + return; + } else { + nghttp2_queue_cell *p = queue->front; + while (p) { + nghttp2_queue_cell *next = p->next; + free(p); + p = next; + } + } +} + +int nghttp2_queue_push(nghttp2_queue *queue, void *data) { + nghttp2_queue_cell *new_cell = + (nghttp2_queue_cell *)malloc(sizeof(nghttp2_queue_cell)); + if (!new_cell) { + return NGHTTP2_ERR_NOMEM; + } + new_cell->data = data; + new_cell->next = NULL; + if (queue->back) { + queue->back->next = new_cell; + queue->back = new_cell; + + } else { + queue->front = queue->back = new_cell; + } + return 0; +} + +void nghttp2_queue_pop(nghttp2_queue *queue) { + nghttp2_queue_cell *front = queue->front; + assert(front); + queue->front = front->next; + if (front == queue->back) { + queue->back = NULL; + } + free(front); +} + +void *nghttp2_queue_front(nghttp2_queue *queue) { + assert(queue->front); + return queue->front->data; +} + +void *nghttp2_queue_back(nghttp2_queue *queue) { + assert(queue->back); + return queue->back->data; +} + +int nghttp2_queue_empty(nghttp2_queue *queue) { return queue->front == NULL; } diff --git a/Utilities/cmnghttp2/lib/nghttp2_queue.h b/Utilities/cmnghttp2/lib/nghttp2_queue.h new file mode 100644 index 00000000000..a06fa6c7a46 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_queue.h @@ -0,0 +1,51 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_QUEUE_H +#define NGHTTP2_QUEUE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct nghttp2_queue_cell { + void *data; + struct nghttp2_queue_cell *next; +} nghttp2_queue_cell; + +typedef struct { + nghttp2_queue_cell *front, *back; +} nghttp2_queue; + +void nghttp2_queue_init(nghttp2_queue *queue); +void nghttp2_queue_free(nghttp2_queue *queue); +int nghttp2_queue_push(nghttp2_queue *queue, void *data); +void nghttp2_queue_pop(nghttp2_queue *queue); +void *nghttp2_queue_front(nghttp2_queue *queue); +void *nghttp2_queue_back(nghttp2_queue *queue); +int nghttp2_queue_empty(nghttp2_queue *queue); + +#endif /* NGHTTP2_QUEUE_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_rcbuf.c b/Utilities/cmnghttp2/lib/nghttp2_rcbuf.c new file mode 100644 index 00000000000..7e7814d2d3c --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_rcbuf.c @@ -0,0 +1,102 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_rcbuf.h" + +#include +#include + +#include "nghttp2_mem.h" +#include "nghttp2_helper.h" + +int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, + nghttp2_mem *mem) { + uint8_t *p; + + p = nghttp2_mem_malloc(mem, sizeof(nghttp2_rcbuf) + size); + if (p == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + *rcbuf_ptr = (void *)p; + + (*rcbuf_ptr)->mem_user_data = mem->mem_user_data; + (*rcbuf_ptr)->free = mem->free; + (*rcbuf_ptr)->base = p + sizeof(nghttp2_rcbuf); + (*rcbuf_ptr)->len = size; + (*rcbuf_ptr)->ref = 1; + + return 0; +} + +int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src, + size_t srclen, nghttp2_mem *mem) { + int rv; + + rv = nghttp2_rcbuf_new(rcbuf_ptr, srclen + 1, mem); + if (rv != 0) { + return rv; + } + + (*rcbuf_ptr)->len = srclen; + *nghttp2_cpymem((*rcbuf_ptr)->base, src, srclen) = '\0'; + + return 0; +} + +/* + * Frees |rcbuf| itself, regardless of its reference cout. + */ +void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf) { + nghttp2_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data); +} + +void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf) { + if (rcbuf->ref == -1) { + return; + } + + ++rcbuf->ref; +} + +void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf) { + if (rcbuf == NULL || rcbuf->ref == -1) { + return; + } + + assert(rcbuf->ref > 0); + + if (--rcbuf->ref == 0) { + nghttp2_rcbuf_del(rcbuf); + } +} + +nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) { + nghttp2_vec res = {rcbuf->base, rcbuf->len}; + return res; +} + +int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) { + return rcbuf->ref == -1; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_rcbuf.h b/Utilities/cmnghttp2/lib/nghttp2_rcbuf.h new file mode 100644 index 00000000000..6814e709fb4 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_rcbuf.h @@ -0,0 +1,80 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_RCBUF_H +#define NGHTTP2_RCBUF_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +struct nghttp2_rcbuf { + /* custom memory allocator belongs to the mem parameter when + creating this object. */ + void *mem_user_data; + nghttp2_free free; + /* The pointer to the underlying buffer */ + uint8_t *base; + /* Size of buffer pointed by |base|. */ + size_t len; + /* Reference count */ + int32_t ref; +}; + +/* + * Allocates nghttp2_rcbuf object with |size| as initial buffer size. + * When the function succeeds, the reference count becomes 1. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM: + * Out of memory. + */ +int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, nghttp2_mem *mem); + +/* + * Like nghttp2_rcbuf_new(), but initializes the buffer with |src| of + * length |srclen|. This function allocates additional byte at the + * end and puts '\0' into it, so that the resulting buffer could be + * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to + * |srclen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM: + * Out of memory. + */ +int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src, + size_t srclen, nghttp2_mem *mem); + +/* + * Frees |rcbuf| itself, regardless of its reference cout. + */ +void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf); + +#endif /* NGHTTP2_RCBUF_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_session.c b/Utilities/cmnghttp2/lib/nghttp2_session.c new file mode 100644 index 00000000000..9df3d6f3293 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_session.c @@ -0,0 +1,7780 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_session.h" + +#include +#include +#include +#include +#include + +#include "nghttp2_helper.h" +#include "nghttp2_net.h" +#include "nghttp2_priority_spec.h" +#include "nghttp2_option.h" +#include "nghttp2_http.h" +#include "nghttp2_pq.h" +#include "nghttp2_debug.h" + +/* + * Returns non-zero if the number of outgoing opened streams is larger + * than or equal to + * remote_settings.max_concurrent_streams. + */ +static int +session_is_outgoing_concurrent_streams_max(nghttp2_session *session) { + return session->remote_settings.max_concurrent_streams <= + session->num_outgoing_streams; +} + +/* + * Returns non-zero if the number of incoming opened streams is larger + * than or equal to + * local_settings.max_concurrent_streams. + */ +static int +session_is_incoming_concurrent_streams_max(nghttp2_session *session) { + return session->local_settings.max_concurrent_streams <= + session->num_incoming_streams; +} + +/* + * Returns non-zero if the number of incoming opened streams is larger + * than or equal to + * session->pending_local_max_concurrent_stream. + */ +static int +session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) { + return session->pending_local_max_concurrent_stream <= + session->num_incoming_streams; +} + +/* + * Returns non-zero if |lib_error| is non-fatal error. + */ +static int is_non_fatal(int lib_error_code) { + return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL; +} + +int nghttp2_is_fatal(int lib_error_code) { + return lib_error_code < NGHTTP2_ERR_FATAL; +} + +static int session_enforce_http_messaging(nghttp2_session *session) { + return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0; +} + +/* + * Returns nonzero if |frame| is trailer headers. + */ +static int session_trailer_headers(nghttp2_session *session, + nghttp2_stream *stream, + nghttp2_frame *frame) { + if (!stream || frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + if (session->server) { + return frame->headers.cat == NGHTTP2_HCAT_HEADERS; + } + + return frame->headers.cat == NGHTTP2_HCAT_HEADERS && + (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0; +} + +/* Returns nonzero if the |stream| is in reserved(remote) state */ +static int state_reserved_remote(nghttp2_session *session, + nghttp2_stream *stream) { + return stream->state == NGHTTP2_STREAM_RESERVED && + !nghttp2_session_is_my_stream_id(session, stream->stream_id); +} + +/* Returns nonzero if the |stream| is in reserved(local) state */ +static int state_reserved_local(nghttp2_session *session, + nghttp2_stream *stream) { + return stream->state == NGHTTP2_STREAM_RESERVED && + nghttp2_session_is_my_stream_id(session, stream->stream_id); +} + +/* + * Checks whether received stream_id is valid. This function returns + * 1 if it succeeds, or 0. + */ +static int session_is_new_peer_stream_id(nghttp2_session *session, + int32_t stream_id) { + return stream_id != 0 && + !nghttp2_session_is_my_stream_id(session, stream_id) && + session->last_recv_stream_id < stream_id; +} + +static int session_detect_idle_stream(nghttp2_session *session, + int32_t stream_id) { + /* Assume that stream object with stream_id does not exist */ + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + if (session->last_sent_stream_id < stream_id) { + return 1; + } + return 0; + } + if (session_is_new_peer_stream_id(session, stream_id)) { + return 1; + } + return 0; +} + +static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) { + return (ext_types[type / 8] & (1 << (type & 0x7))) > 0; +} + +static int session_call_error_callback(nghttp2_session *session, + int lib_error_code, const char *fmt, + ...) { + size_t bufsize; + va_list ap; + char *buf; + int rv; + nghttp2_mem *mem; + + if (!session->callbacks.error_callback && + !session->callbacks.error_callback2) { + return 0; + } + + mem = &session->mem; + + va_start(ap, fmt); + rv = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + if (rv < 0) { + return NGHTTP2_ERR_NOMEM; + } + + bufsize = (size_t)(rv + 1); + + buf = nghttp2_mem_malloc(mem, bufsize); + if (buf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + va_start(ap, fmt); + rv = vsnprintf(buf, bufsize, fmt, ap); + va_end(ap); + + if (rv < 0) { + nghttp2_mem_free(mem, buf); + /* vsnprintf may return error because of various things we can + imagine, but typically we don't want to drop session just for + debug callback. */ + DEBUGF("error_callback: vsnprintf failed. The template was %s\n", fmt); + return 0; + } + + if (session->callbacks.error_callback2) { + rv = session->callbacks.error_callback2(session, lib_error_code, buf, + (size_t)rv, session->user_data); + } else { + rv = session->callbacks.error_callback(session, buf, (size_t)rv, + session->user_data); + } + + nghttp2_mem_free(mem, buf); + + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int session_terminate_session(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code, const char *reason) { + int rv; + const uint8_t *debug_data; + size_t debug_datalen; + + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return 0; + } + + /* Ignore all incoming frames because we are going to tear down the + session. */ + session->iframe.state = NGHTTP2_IB_IGN_ALL; + + if (reason == NULL) { + debug_data = NULL; + debug_datalen = 0; + } else { + debug_data = (const uint8_t *)reason; + debug_datalen = strlen(reason); + } + + rv = nghttp2_session_add_goaway(session, last_stream_id, error_code, + debug_data, debug_datalen, + NGHTTP2_GOAWAY_AUX_TERM_ON_SEND); + + if (rv != 0) { + return rv; + } + + session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND; + + return 0; +} + +int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code) { + return session_terminate_session(session, session->last_proc_stream_id, + error_code, NULL); +} + +int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code) { + return session_terminate_session(session, last_stream_id, error_code, NULL); +} + +int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, + uint32_t error_code, + const char *reason) { + return session_terminate_session(session, session->last_proc_stream_id, + error_code, reason); +} + +int nghttp2_session_is_my_stream_id(nghttp2_session *session, + int32_t stream_id) { + int rem; + if (stream_id == 0) { + return 0; + } + rem = stream_id & 0x1; + if (session->server) { + return rem == 0; + } + return rem == 1; +} + +nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); + + if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) || + stream->state == NGHTTP2_STREAM_IDLE) { + return NULL; + } + + return stream; +} + +nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, + int32_t stream_id) { + return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); +} + +static void session_inbound_frame_reset(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_mem *mem = &session->mem; + /* A bit risky code, since if this function is called from + nghttp2_session_new(), we rely on the fact that + iframe->frame.hd.type is 0, so that no free is performed. */ + switch (iframe->frame.hd.type) { + case NGHTTP2_DATA: + break; + case NGHTTP2_HEADERS: + nghttp2_frame_headers_free(&iframe->frame.headers, mem); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_priority_free(&iframe->frame.priority); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream); + break; + case NGHTTP2_SETTINGS: + nghttp2_frame_settings_free(&iframe->frame.settings, mem); + + nghttp2_mem_free(mem, iframe->iv); + + iframe->iv = NULL; + iframe->niv = 0; + iframe->max_niv = 0; + + break; + case NGHTTP2_PUSH_PROMISE: + nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem); + break; + case NGHTTP2_PING: + nghttp2_frame_ping_free(&iframe->frame.ping); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_goaway_free(&iframe->frame.goaway, mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_window_update_free(&iframe->frame.window_update); + break; + default: + /* extension frame */ + if (check_ext_type_set(session->user_recv_ext_types, + iframe->frame.hd.type)) { + nghttp2_frame_extension_free(&iframe->frame.ext); + } else { + switch (iframe->frame.hd.type) { + case NGHTTP2_ALTSVC: + if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) { + break; + } + nghttp2_frame_altsvc_free(&iframe->frame.ext, mem); + break; + case NGHTTP2_ORIGIN: + if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) { + break; + } + nghttp2_frame_origin_free(&iframe->frame.ext, mem); + break; + } + } + + break; + } + + memset(&iframe->frame, 0, sizeof(nghttp2_frame)); + memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload)); + + iframe->state = NGHTTP2_IB_READ_HEAD; + + nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf, + sizeof(iframe->raw_sbuf)); + iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN; + + nghttp2_buf_free(&iframe->lbuf, mem); + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + iframe->raw_lbuf = NULL; + + iframe->payloadleft = 0; + iframe->padlen = 0; +} + +static void init_settings(nghttp2_settings_storage *settings) { + settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + settings->enable_push = 1; + settings->max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; + settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; + settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; + settings->max_header_list_size = UINT32_MAX; +} + +static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, + nghttp2_mem *mem) { + DEBUGF("send: reset nghttp2_active_outbound_item\n"); + DEBUGF("send: aob->item = %p\n", aob->item); + nghttp2_outbound_item_free(aob->item, mem); + nghttp2_mem_free(mem, aob->item); + aob->item = NULL; + nghttp2_bufs_reset(&aob->framebufs); + aob->state = NGHTTP2_OB_POP_ITEM; +} + +int nghttp2_enable_strict_preface = 1; + +static int session_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, int server, + const nghttp2_option *option, nghttp2_mem *mem) { + int rv; + size_t nbuffer; + size_t max_deflate_dynamic_table_size = + NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session)); + if (*session_ptr == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail_session; + } + + (*session_ptr)->mem = *mem; + mem = &(*session_ptr)->mem; + + /* next_stream_id is initialized in either + nghttp2_session_client_new2 or nghttp2_session_server_new2 */ + + nghttp2_stream_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE, + NGHTTP2_STREAM_IDLE, NGHTTP2_DEFAULT_WEIGHT, 0, 0, NULL, + mem); + + (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + (*session_ptr)->recv_window_size = 0; + (*session_ptr)->consumed_size = 0; + (*session_ptr)->recv_reduction = 0; + (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + + (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE; + (*session_ptr)->local_last_stream_id = (1u << 31) - 1; + (*session_ptr)->remote_last_stream_id = (1u << 31) - 1; + + (*session_ptr)->pending_local_max_concurrent_stream = + NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; + (*session_ptr)->pending_enable_push = 1; + + if (server) { + (*session_ptr)->server = 1; + } + + init_settings(&(*session_ptr)->remote_settings); + init_settings(&(*session_ptr)->local_settings); + + (*session_ptr)->max_incoming_reserved_streams = + NGHTTP2_MAX_INCOMING_RESERVED_STREAMS; + + /* Limit max outgoing concurrent streams to sensible value */ + (*session_ptr)->remote_settings.max_concurrent_streams = 100; + + (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN; + (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; + + if (option) { + if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && + option->no_auto_window_update) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE; + } + + if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) { + + (*session_ptr)->remote_settings.max_concurrent_streams = + option->peer_max_concurrent_streams; + } + + if (option->opt_set_mask & NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS) { + + (*session_ptr)->max_incoming_reserved_streams = + option->max_reserved_remote_streams; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) && + option->no_recv_client_magic) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) && + option->no_http_messaging) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; + } + + if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) { + memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types, + sizeof((*session_ptr)->user_recv_ext_types)); + } + + if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) { + (*session_ptr)->builtin_recv_ext_types = option->builtin_recv_ext_types; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) && + option->no_auto_ping_ack) { + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK; + } + + if (option->opt_set_mask & NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH) { + (*session_ptr)->max_send_header_block_length = + option->max_send_header_block_length; + } + + if (option->opt_set_mask & NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE) { + max_deflate_dynamic_table_size = option->max_deflate_dynamic_table_size; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_CLOSED_STREAMS) && + option->no_closed_streams) { + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS; + } + + if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) { + (*session_ptr)->max_outbound_ack = option->max_outbound_ack; + } + } + + rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, + max_deflate_dynamic_table_size, mem); + if (rv != 0) { + goto fail_hd_deflater; + } + rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem); + if (rv != 0) { + goto fail_hd_inflater; + } + rv = nghttp2_map_init(&(*session_ptr)->streams, mem); + if (rv != 0) { + goto fail_map; + } + + nbuffer = ((*session_ptr)->max_send_header_block_length + + NGHTTP2_FRAMEBUF_CHUNKLEN - 1) / + NGHTTP2_FRAMEBUF_CHUNKLEN; + + if (nbuffer == 0) { + nbuffer = 1; + } + + /* 1 for Pad Field. */ + rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs, + NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1, + NGHTTP2_FRAME_HDLEN + 1, mem); + if (rv != 0) { + goto fail_aob_framebuf; + } + + active_outbound_item_reset(&(*session_ptr)->aob, mem); + + (*session_ptr)->callbacks = *callbacks; + (*session_ptr)->user_data = user_data; + + session_inbound_frame_reset(*session_ptr); + + if (nghttp2_enable_strict_preface) { + nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe; + + if (server && ((*session_ptr)->opt_flags & + NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) { + iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC; + iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN; + } else { + iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; + } + + if (!server) { + (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC; + nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC, + NGHTTP2_CLIENT_MAGIC_LEN); + } + } + + return 0; + +fail_aob_framebuf: + nghttp2_map_free(&(*session_ptr)->streams); +fail_map: + nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater); +fail_hd_inflater: + nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); +fail_hd_deflater: + nghttp2_mem_free(mem, *session_ptr); +fail_session: + return rv; +} + +int nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) { + return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL, + NULL); +} + +int nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option) { + return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option, + NULL); +} + +int nghttp2_session_client_new3(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, + nghttp2_mem *mem) { + int rv; + nghttp2_session *session; + + rv = session_new(&session, callbacks, user_data, 0, option, mem); + + if (rv != 0) { + return rv; + } + /* IDs for use in client */ + session->next_stream_id = 1; + + *session_ptr = session; + + return 0; +} + +int nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) { + return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL, + NULL); +} + +int nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option) { + return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option, + NULL); +} + +int nghttp2_session_server_new3(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, + nghttp2_mem *mem) { + int rv; + nghttp2_session *session; + + rv = session_new(&session, callbacks, user_data, 1, option, mem); + + if (rv != 0) { + return rv; + } + /* IDs for use in client */ + session->next_stream_id = 2; + + *session_ptr = session; + + return 0; +} + +static int free_streams(nghttp2_map_entry *entry, void *ptr) { + nghttp2_session *session; + nghttp2_stream *stream; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + session = (nghttp2_session *)ptr; + mem = &session->mem; + stream = (nghttp2_stream *)entry; + item = stream->item; + + if (item && !item->queued && item != session->aob.item) { + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + } + + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); + + return 0; +} + +static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) { + nghttp2_outbound_item *item, *next; + for (item = q->head; item;) { + next = item->qnext; + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + item = next; + } +} + +static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr, + const nghttp2_settings_entry *iv, size_t niv, + nghttp2_mem *mem) { + *settings_ptr = nghttp2_mem_malloc(mem, sizeof(nghttp2_inflight_settings)); + if (!*settings_ptr) { + return NGHTTP2_ERR_NOMEM; + } + + if (niv > 0) { + (*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem); + if (!(*settings_ptr)->iv) { + nghttp2_mem_free(mem, *settings_ptr); + return NGHTTP2_ERR_NOMEM; + } + } else { + (*settings_ptr)->iv = NULL; + } + + (*settings_ptr)->niv = niv; + (*settings_ptr)->next = NULL; + + return 0; +} + +static void inflight_settings_del(nghttp2_inflight_settings *settings, + nghttp2_mem *mem) { + if (!settings) { + return; + } + + nghttp2_mem_free(mem, settings->iv); + nghttp2_mem_free(mem, settings); +} + +void nghttp2_session_del(nghttp2_session *session) { + nghttp2_mem *mem; + nghttp2_inflight_settings *settings; + + if (session == NULL) { + return; + } + + mem = &session->mem; + + for (settings = session->inflight_settings_head; settings;) { + nghttp2_inflight_settings *next = settings->next; + inflight_settings_del(settings, mem); + settings = next; + } + + nghttp2_stream_free(&session->root); + + /* Have to free streams first, so that we can check + stream->item->queued */ + nghttp2_map_each_free(&session->streams, free_streams, session); + nghttp2_map_free(&session->streams); + + ob_q_free(&session->ob_urgent, mem); + ob_q_free(&session->ob_reg, mem); + ob_q_free(&session->ob_syn, mem); + + active_outbound_item_reset(&session->aob, mem); + session_inbound_frame_reset(session); + nghttp2_hd_deflate_free(&session->hd_deflater); + nghttp2_hd_inflate_free(&session->hd_inflater); + nghttp2_bufs_free(&session->aob.framebufs); + nghttp2_mem_free(mem, session); +} + +int nghttp2_session_reprioritize_stream( + nghttp2_session *session, nghttp2_stream *stream, + const nghttp2_priority_spec *pri_spec_in) { + int rv; + nghttp2_stream *dep_stream = NULL; + nghttp2_priority_spec pri_spec_default; + const nghttp2_priority_spec *pri_spec = pri_spec_in; + + assert(pri_spec->stream_id != stream->stream_id); + + if (!nghttp2_stream_in_dep_tree(stream)) { + return 0; + } + + if (pri_spec->stream_id != 0) { + dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); + + if (!dep_stream && + session_detect_idle_stream(session, pri_spec->stream_id)) { + + nghttp2_priority_spec_default_init(&pri_spec_default); + + dep_stream = nghttp2_session_open_stream( + session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + if (dep_stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { + nghttp2_priority_spec_default_init(&pri_spec_default); + pri_spec = &pri_spec_default; + } + } + + if (pri_spec->stream_id == 0) { + dep_stream = &session->root; + } else if (nghttp2_stream_dep_find_ancestor(dep_stream, stream)) { + DEBUGF("stream: cycle detected, dep_stream(%p)=%d stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id); + + nghttp2_stream_dep_remove_subtree(dep_stream); + rv = nghttp2_stream_dep_add_subtree(stream->dep_prev, dep_stream); + if (rv != 0) { + return rv; + } + } + + assert(dep_stream); + + if (dep_stream == stream->dep_prev && !pri_spec->exclusive) { + /* This is minor optimization when just weight is changed. */ + nghttp2_stream_change_weight(stream, pri_spec->weight); + + return 0; + } + + nghttp2_stream_dep_remove_subtree(stream); + + /* We have to update weight after removing stream from tree */ + stream->weight = pri_spec->weight; + + if (pri_spec->exclusive) { + rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream); + } else { + rv = nghttp2_stream_dep_add_subtree(dep_stream, stream); + } + + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item) { + /* TODO Return error if stream is not found for the frame requiring + stream presence. */ + int rv = 0; + nghttp2_stream *stream; + nghttp2_frame *frame; + + frame = &item->frame; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + switch (frame->hd.type) { + case NGHTTP2_DATA: + if (!stream) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + + if (stream->item) { + return NGHTTP2_ERR_DATA_EXIST; + } + + rv = nghttp2_stream_attach_item(stream, item); + + if (rv != 0) { + return rv; + } + + return 0; + case NGHTTP2_HEADERS: + /* We push request HEADERS and push response HEADERS to + dedicated queue because their transmission is affected by + SETTINGS_MAX_CONCURRENT_STREAMS */ + /* TODO If 2 HEADERS are submitted for reserved stream, then + both of them are queued into ob_syn, which is not + desirable. */ + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST || + (stream && stream->state == NGHTTP2_STREAM_RESERVED)) { + nghttp2_outbound_queue_push(&session->ob_syn, item); + item->queued = 1; + return 0; + ; + } + + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + case NGHTTP2_SETTINGS: + case NGHTTP2_PING: + nghttp2_outbound_queue_push(&session->ob_urgent, item); + item->queued = 1; + return 0; + case NGHTTP2_RST_STREAM: + if (stream) { + stream->state = NGHTTP2_STREAM_CLOSING; + } + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + case NGHTTP2_PUSH_PROMISE: { + nghttp2_headers_aux_data *aux_data; + nghttp2_priority_spec pri_spec; + + aux_data = &item->aux_data.headers; + + if (!stream) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + + nghttp2_priority_spec_init(&pri_spec, stream->stream_id, + NGHTTP2_DEFAULT_WEIGHT, 0); + + if (!nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, + NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED, + aux_data->stream_user_data)) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't have to call nghttp2_session_adjust_closed_stream() + here, since stream->stream_id is local stream_id, and it does + not affect closed stream count. */ + + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + + return 0; + } + case NGHTTP2_WINDOW_UPDATE: + if (stream) { + stream->window_update_queued = 1; + } else if (frame->hd.stream_id == 0) { + session->window_update_queued = 1; + } + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + default: + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + } +} + +int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream && stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + + /* Cancel pending request HEADERS in ob_syn if this RST_STREAM + refers to that stream. */ + if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) && + nghttp2_outbound_queue_top(&session->ob_syn)) { + nghttp2_headers_aux_data *aux_data; + nghttp2_frame *headers_frame; + + headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; + assert(headers_frame->hd.type == NGHTTP2_HEADERS); + + if (headers_frame->hd.stream_id <= stream_id && + (uint32_t)stream_id < session->next_stream_id) { + + for (item = session->ob_syn.head; item; item = item->qnext) { + aux_data = &item->aux_data.headers; + + if (item->frame.hd.stream_id < stream_id) { + continue; + } + + /* stream_id in ob_syn queue must be strictly increasing. If + we found larger ID, then we can break here. */ + if (item->frame.hd.stream_id > stream_id || aux_data->canceled) { + break; + } + + aux_data->error_code = error_code; + aux_data->canceled = 1; + + return 0; + } + } + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code); + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_rst_stream_free(&frame->rst_stream); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, + nghttp2_priority_spec *pri_spec_in, + nghttp2_stream_state initial_state, + void *stream_user_data) { + int rv; + nghttp2_stream *stream; + nghttp2_stream *dep_stream = NULL; + int stream_alloc = 0; + nghttp2_priority_spec pri_spec_default; + nghttp2_priority_spec *pri_spec = pri_spec_in; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream_raw(session, stream_id); + + if (stream) { + assert(stream->state == NGHTTP2_STREAM_IDLE); + assert(nghttp2_stream_in_dep_tree(stream)); + nghttp2_session_detach_idle_stream(session, stream); + rv = nghttp2_stream_dep_remove(stream); + if (rv != 0) { + return NULL; + } + } else { + stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); + if (stream == NULL) { + return NULL; + } + + stream_alloc = 1; + } + + if (pri_spec->stream_id != 0) { + dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); + + if (!dep_stream && + session_detect_idle_stream(session, pri_spec->stream_id)) { + /* Depends on idle stream, which does not exist in memory. + Assign default priority for it. */ + nghttp2_priority_spec_default_init(&pri_spec_default); + + dep_stream = nghttp2_session_open_stream( + session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + if (dep_stream == NULL) { + if (stream_alloc) { + nghttp2_mem_free(mem, stream); + } + + return NULL; + } + } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { + /* If dep_stream is not part of dependency tree, stream will get + default priority. This handles the case when + pri_spec->stream_id == stream_id. This happens because we + don't check pri_spec->stream_id against new stream ID in + nghttp2_submit_request. This also handles the case when idle + stream created by PRIORITY frame was opened. Somehow we + first remove the idle stream from dependency tree. This is + done to simplify code base, but ideally we should retain old + dependency. But I'm not sure this adds values. */ + nghttp2_priority_spec_default_init(&pri_spec_default); + pri_spec = &pri_spec_default; + } + } + + if (initial_state == NGHTTP2_STREAM_RESERVED) { + flags |= NGHTTP2_STREAM_FLAG_PUSH; + } + + if (stream_alloc) { + nghttp2_stream_init(stream, stream_id, flags, initial_state, + pri_spec->weight, + (int32_t)session->remote_settings.initial_window_size, + (int32_t)session->local_settings.initial_window_size, + stream_user_data, mem); + + rv = nghttp2_map_insert(&session->streams, &stream->map_entry); + if (rv != 0) { + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); + return NULL; + } + } else { + stream->flags = flags; + stream->state = initial_state; + stream->weight = pri_spec->weight; + stream->stream_user_data = stream_user_data; + } + + switch (initial_state) { + case NGHTTP2_STREAM_RESERVED: + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + /* reserved (local) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } else { + /* reserved (remote) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + ++session->num_incoming_reserved_streams; + } + /* Reserved stream does not count in the concurrent streams + limit. That is one of the DOS vector. */ + break; + case NGHTTP2_STREAM_IDLE: + /* Idle stream does not count toward the concurrent streams limit. + This is used as anchor node in dependency tree. */ + nghttp2_session_keep_idle_stream(session, stream); + break; + default: + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + ++session->num_outgoing_streams; + } else { + ++session->num_incoming_streams; + } + } + + if (pri_spec->stream_id == 0) { + dep_stream = &session->root; + } + + assert(dep_stream); + + if (pri_spec->exclusive) { + rv = nghttp2_stream_dep_insert(dep_stream, stream); + if (rv != 0) { + return NULL; + } + } else { + nghttp2_stream_dep_add(dep_stream, stream); + } + + return stream; +} + +int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + int rv; + nghttp2_stream *stream; + nghttp2_mem *mem; + int is_my_stream_id; + + mem = &session->mem; + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + DEBUGF("stream: stream(%p)=%d close\n", stream, stream->stream_id); + + if (stream->item) { + nghttp2_outbound_item *item; + + item = stream->item; + + rv = nghttp2_stream_detach_item(stream); + + if (rv != 0) { + return rv; + } + + /* If item is queued, it will be deleted when it is popped + (nghttp2_session_prep_frame() will fail). If session->aob.item + points to this item, let active_outbound_item_reset() + free the item. */ + if (!item->queued && item != session->aob.item) { + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + } + } + + /* We call on_stream_close_callback even if stream->state is + NGHTTP2_STREAM_INITIAL. This will happen while sending request + HEADERS, a local endpoint receives RST_STREAM for that stream. It + may be PROTOCOL_ERROR, but without notifying stream closure will + hang the stream in a local endpoint. + */ + + if (session->callbacks.on_stream_close_callback) { + if (session->callbacks.on_stream_close_callback( + session, stream_id, error_code, session->user_data) != 0) { + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + is_my_stream_id = nghttp2_session_is_my_stream_id(session, stream_id); + + /* pushed streams which is not opened yet is not counted toward max + concurrent limits */ + if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH)) { + if (!is_my_stream_id) { + --session->num_incoming_reserved_streams; + } + } else { + if (is_my_stream_id) { + --session->num_outgoing_streams; + } else { + --session->num_incoming_streams; + } + } + + /* Closes both directions just in case they are not closed yet */ + stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; + + if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 && + session->server && !is_my_stream_id && + nghttp2_stream_in_dep_tree(stream)) { + /* On server side, retain stream at most MAX_CONCURRENT_STREAMS + combined with the current active incoming streams to make + dependency tree work better. */ + nghttp2_session_keep_closed_stream(session, stream); + } else { + rv = nghttp2_session_destroy_stream(session, stream); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +int nghttp2_session_destroy_stream(nghttp2_session *session, + nghttp2_stream *stream) { + nghttp2_mem *mem; + int rv; + + DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id); + + mem = &session->mem; + + if (nghttp2_stream_in_dep_tree(stream)) { + rv = nghttp2_stream_dep_remove(stream); + if (rv != 0) { + return rv; + } + } + + nghttp2_map_remove(&session->streams, stream->stream_id); + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); + + return 0; +} + +void nghttp2_session_keep_closed_stream(nghttp2_session *session, + nghttp2_stream *stream) { + DEBUGF("stream: keep closed stream(%p)=%d, state=%d\n", stream, + stream->stream_id, stream->state); + + if (session->closed_stream_tail) { + session->closed_stream_tail->closed_next = stream; + stream->closed_prev = session->closed_stream_tail; + } else { + session->closed_stream_head = stream; + } + session->closed_stream_tail = stream; + + ++session->num_closed_streams; +} + +void nghttp2_session_keep_idle_stream(nghttp2_session *session, + nghttp2_stream *stream) { + DEBUGF("stream: keep idle stream(%p)=%d, state=%d\n", stream, + stream->stream_id, stream->state); + + if (session->idle_stream_tail) { + session->idle_stream_tail->closed_next = stream; + stream->closed_prev = session->idle_stream_tail; + } else { + session->idle_stream_head = stream; + } + session->idle_stream_tail = stream; + + ++session->num_idle_streams; +} + +void nghttp2_session_detach_idle_stream(nghttp2_session *session, + nghttp2_stream *stream) { + nghttp2_stream *prev_stream, *next_stream; + + DEBUGF("stream: detach idle stream(%p)=%d, state=%d\n", stream, + stream->stream_id, stream->state); + + prev_stream = stream->closed_prev; + next_stream = stream->closed_next; + + if (prev_stream) { + prev_stream->closed_next = next_stream; + } else { + session->idle_stream_head = next_stream; + } + + if (next_stream) { + next_stream->closed_prev = prev_stream; + } else { + session->idle_stream_tail = prev_stream; + } + + stream->closed_prev = NULL; + stream->closed_next = NULL; + + --session->num_idle_streams; +} + +int nghttp2_session_adjust_closed_stream(nghttp2_session *session) { + size_t num_stream_max; + int rv; + + if (session->local_settings.max_concurrent_streams == + NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS) { + num_stream_max = session->pending_local_max_concurrent_stream; + } else { + num_stream_max = session->local_settings.max_concurrent_streams; + } + + DEBUGF("stream: adjusting kept closed streams num_closed_streams=%zu, " + "num_incoming_streams=%zu, max_concurrent_streams=%zu\n", + session->num_closed_streams, session->num_incoming_streams, + num_stream_max); + + while (session->num_closed_streams > 0 && + session->num_closed_streams + session->num_incoming_streams > + num_stream_max) { + nghttp2_stream *head_stream; + nghttp2_stream *next; + + head_stream = session->closed_stream_head; + + assert(head_stream); + + next = head_stream->closed_next; + + rv = nghttp2_session_destroy_stream(session, head_stream); + if (rv != 0) { + return rv; + } + + /* head_stream is now freed */ + + session->closed_stream_head = next; + + if (session->closed_stream_head) { + session->closed_stream_head->closed_prev = NULL; + } else { + session->closed_stream_tail = NULL; + } + + --session->num_closed_streams; + } + + return 0; +} + +int nghttp2_session_adjust_idle_stream(nghttp2_session *session) { + size_t max; + int rv; + + /* Make minimum number of idle streams 16, and maximum 100, which + are arbitrary chosen numbers. */ + max = nghttp2_min( + 100, nghttp2_max( + 16, nghttp2_min(session->local_settings.max_concurrent_streams, + session->pending_local_max_concurrent_stream))); + + DEBUGF("stream: adjusting kept idle streams num_idle_streams=%zu, max=%zu\n", + session->num_idle_streams, max); + + while (session->num_idle_streams > max) { + nghttp2_stream *head; + nghttp2_stream *next; + + head = session->idle_stream_head; + assert(head); + + next = head->closed_next; + + rv = nghttp2_session_destroy_stream(session, head); + if (rv != 0) { + return rv; + } + + /* head is now destroyed */ + + session->idle_stream_head = next; + + if (session->idle_stream_head) { + session->idle_stream_head->closed_prev = NULL; + } else { + session->idle_stream_tail = NULL; + } + + --session->num_idle_streams; + } + + return 0; +} + +/* + * Closes stream with stream ID |stream_id| if both transmission and + * reception of the stream were disallowed. The |error_code| indicates + * the reason of the closure. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The stream is not found. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, + nghttp2_stream *stream) { + if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) { + return nghttp2_session_close_stream(session, stream->stream_id, + NGHTTP2_NO_ERROR); + } + return 0; +} + +/* + * Returns nonzero if local endpoint allows reception of new stream + * from remote. + */ +static int session_allow_incoming_new_stream(nghttp2_session *session) { + return (session->goaway_flags & + (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_SENT)) == 0; +} + +/* + * This function returns nonzero if session is closing. + */ +static int session_is_closing(nghttp2_session *session) { + return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 || + (nghttp2_session_want_read(session) == 0 && + nghttp2_session_want_write(session) == 0); +} + +/* + * Check that we can send a frame to the |stream|. This function + * returns 0 if we can send a frame to the |frame|, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The stream is half-closed for transmission. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_for_stream_send(nghttp2_session *session, + nghttp2_stream *stream) { + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return NGHTTP2_ERR_STREAM_SHUT_WR; + } + return 0; +} + +int nghttp2_session_check_request_allowed(nghttp2_session *session) { + return !session->server && session->next_stream_id <= INT32_MAX && + (session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 && + !session_is_closing(session); +} + +/* + * This function checks request HEADERS frame, which opens stream, can + * be sent at this time. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because of GOAWAY: session is + * going down or received last_stream_id is strictly less than + * frame->hd.stream_id. + * NGHTTP2_ERR_STREAM_CLOSING + * request HEADERS was canceled by RST_STREAM while it is in queue. + */ +static int session_predicate_request_headers_send(nghttp2_session *session, + nghttp2_outbound_item *item) { + if (item->aux_data.headers.canceled) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND), + GOAWAY was received from peer, or session is about to close, new + request is not allowed. */ + if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) || + session_is_closing(session)) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks HEADERS, which is the first frame from the + * server, with the |stream| can be sent at this time. The |stream| + * can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_INVALID_STREAM_ID + * The stream ID is invalid. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + * NGHTTP2_ERR_PROTO + * Client side attempted to send response. + */ +static int session_predicate_response_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + return NGHTTP2_ERR_INVALID_STREAM_ID; + } + switch (stream->state) { + case NGHTTP2_STREAM_OPENING: + return 0; + case NGHTTP2_STREAM_CLOSING: + return NGHTTP2_ERR_STREAM_CLOSING; + default: + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } +} + +/* + * This function checks HEADERS for reserved stream can be sent. The + * |stream| must be reserved state and the |session| is server side. + * The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The stream is half-closed for transmission. + * NGHTTP2_ERR_PROTO + * The stream is not reserved state + * NGHTTP2_ERR_STREAM_CLOSED + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because GOAWAY is already sent or + * received. + * NGHTTP2_ERR_PROTO + * Client side attempted to send push response. + */ +static int +session_predicate_push_response_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + /* TODO Should disallow HEADERS if GOAWAY has already been issued? */ + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + if (stream->state != NGHTTP2_STREAM_RESERVED) { + return NGHTTP2_ERR_PROTO; + } + if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks HEADERS, which is neither stream-opening nor + * first response header, with the |stream| can be sent at this time. + * The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + + switch (stream->state) { + case NGHTTP2_STREAM_OPENED: + return 0; + case NGHTTP2_STREAM_CLOSING: + return NGHTTP2_ERR_STREAM_CLOSING; + default: + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + return 0; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } +} + +/* + * This function checks PUSH_PROMISE frame |frame| with the |stream| + * can be sent at this time. The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because GOAWAY is already sent or + * received. + * NGHTTP2_ERR_PROTO + * The client side attempts to send PUSH_PROMISE, or the server + * sends PUSH_PROMISE for the stream not initiated by the client. + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_PUSH_DISABLED + * The remote peer disabled reception of PUSH_PROMISE. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_push_promise_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + + assert(stream); + + if (session->remote_settings.enable_push == 0) { + return NGHTTP2_ERR_PUSH_DISABLED; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks WINDOW_UPDATE with the stream ID |stream_id| + * can be sent at this time. Note that END_STREAM flag of the previous + * frame does not affect the transmission of the WINDOW_UPDATE frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_window_update_send(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + + if (stream_id == 0) { + /* Connection-level window update */ + return 0; + } + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (state_reserved_local(session, stream)) { + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } + return 0; +} + +static int session_predicate_altsvc_send(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + + if (stream_id == 0) { + return 0; + } + + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + + return 0; +} + +static int session_predicate_origin_send(nghttp2_session *session) { + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + return 0; +} + +/* Take into account settings max frame size and both connection-level + flow control here */ +static ssize_t +nghttp2_session_enforce_flow_control_limits(nghttp2_session *session, + nghttp2_stream *stream, + ssize_t requested_window_size) { + DEBUGF("send: remote windowsize connection=%d, remote maxframsize=%u, " + "stream(id %d)=%d\n", + session->remote_window_size, session->remote_settings.max_frame_size, + stream->stream_id, stream->remote_window_size); + + return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size, + stream->remote_window_size), + session->remote_window_size), + (int32_t)session->remote_settings.max_frame_size); +} + +/* + * Returns the maximum length of next data read. If the + * connection-level and/or stream-wise flow control are enabled, the + * return value takes into account those current window sizes. The remote + * settings for max frame size is also taken into account. + */ +static size_t nghttp2_session_next_data_read(nghttp2_session *session, + nghttp2_stream *stream) { + ssize_t window_size; + + window_size = nghttp2_session_enforce_flow_control_limits( + session, stream, NGHTTP2_DATA_PAYLOADLEN); + + DEBUGF("send: available window=%zd\n", window_size); + + return window_size > 0 ? (size_t)window_size : 0; +} + +/* + * This function checks DATA with the |stream| can be sent at this + * time. The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int nghttp2_session_predicate_data_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + /* Request body data */ + /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was + queued but not yet sent. In this case, we won't send DATA + frames. */ + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } + return 0; + } + /* Response body data */ + if (stream->state == NGHTTP2_STREAM_OPENED) { + return 0; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; +} + +static ssize_t session_call_select_padding(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen) { + ssize_t rv; + + if (frame->hd.length >= max_payloadlen) { + return (ssize_t)frame->hd.length; + } + + if (session->callbacks.select_padding_callback) { + size_t max_paddedlen; + + max_paddedlen = + nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen); + + rv = session->callbacks.select_padding_callback( + session, frame, max_paddedlen, session->user_data); + if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return rv; + } + return (ssize_t)frame->hd.length; +} + +/* Add padding to HEADERS or PUSH_PROMISE. We use + frame->headers.padlen in this function to use the fact that + frame->push_promise has also padlen in the same position. */ +static int session_headers_add_pad(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + ssize_t padded_payloadlen; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + size_t padlen; + size_t max_payloadlen; + + aob = &session->aob; + framebufs = &aob->framebufs; + + max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN, + frame->hd.length + NGHTTP2_MAX_PADLEN); + + padded_payloadlen = + session_call_select_padding(session, frame, max_payloadlen); + + if (nghttp2_is_fatal((int)padded_payloadlen)) { + return (int)padded_payloadlen; + } + + padlen = (size_t)padded_payloadlen - frame->hd.length; + + DEBUGF("send: padding selected: payloadlen=%zd, padlen=%zu\n", + padded_payloadlen, padlen); + + rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0); + + if (rv != 0) { + return rv; + } + + frame->headers.padlen = padlen; + + return 0; +} + +static size_t session_estimate_headers_payload(nghttp2_session *session, + const nghttp2_nv *nva, + size_t nvlen, + size_t additional) { + return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) + + additional; +} + +static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs, + nghttp2_frame *frame) { + ssize_t rv; + nghttp2_buf *buf; + size_t buflen; + size_t framelen; + + assert(session->callbacks.pack_extension_callback); + + buf = &bufs->head->buf; + buflen = nghttp2_min(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN); + + rv = session->callbacks.pack_extension_callback(session, buf->last, buflen, + frame, session->user_data); + if (rv == NGHTTP2_ERR_CANCEL) { + return (int)rv; + } + + if (rv < 0 || (size_t)rv > buflen) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + framelen = (size_t)rv; + + frame->hd.length = framelen; + + assert(buf->pos == buf->last); + buf->last += framelen; + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + return 0; +} + +/* + * This function serializes frame for transmission. + * + * This function returns 0 if it succeeds, or one of negative error + * codes, including both fatal and non-fatal ones. + */ +static int session_prep_frame(nghttp2_session *session, + nghttp2_outbound_item *item) { + int rv; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + frame = &item->frame; + + switch (frame->hd.type) { + case NGHTTP2_DATA: { + size_t next_readmax; + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (stream) { + assert(stream->item == item); + } + + rv = nghttp2_session_predicate_data_send(session, stream); + if (rv != 0) { + // If stream was already closed, nghttp2_session_get_stream() + // returns NULL, but item is still attached to the stream. + // Search stream including closed again. + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + if (stream) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + return rv; + } + /* Assuming stream is not NULL */ + assert(stream); + next_readmax = nghttp2_session_next_data_read(session, stream); + + if (next_readmax == 0) { + + /* This must be true since we only pop DATA frame item from + queue when session->remote_window_size > 0 */ + assert(session->remote_window_size > 0); + + rv = nghttp2_stream_defer_item(stream, + NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); + return NGHTTP2_ERR_DEFERRED; + } + + rv = nghttp2_session_pack_data(session, &session->aob.framebufs, + next_readmax, frame, &item->aux_data.data, + stream); + if (rv == NGHTTP2_ERR_PAUSE) { + return rv; + } + if (rv == NGHTTP2_ERR_DEFERRED) { + rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); + return NGHTTP2_ERR_DEFERRED; + } + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + if (rv != 0) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + + return rv; + } + return 0; + } + case NGHTTP2_HEADERS: { + nghttp2_headers_aux_data *aux_data; + size_t estimated_payloadlen; + + aux_data = &item->aux_data.headers; + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + /* initial HEADERS, which opens stream */ + nghttp2_stream *stream; + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL, + aux_data->stream_user_data); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't call nghttp2_session_adjust_closed_stream() here, + since we don't keep closed stream in client side */ + + rv = session_predicate_request_headers_send(session, item); + if (rv != 0) { + return rv; + } + + if (session_enforce_http_messaging(session)) { + nghttp2_http_record_request_method(stream, frame); + } + } else { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (stream && stream->state == NGHTTP2_STREAM_RESERVED) { + rv = session_predicate_push_response_headers_send(session, stream); + if (rv == 0) { + frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; + + if (aux_data->stream_user_data) { + stream->stream_user_data = aux_data->stream_user_data; + } + } + } else if (session_predicate_response_headers_send(session, stream) == + 0) { + frame->headers.cat = NGHTTP2_HCAT_RESPONSE; + rv = 0; + } else { + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + + rv = session_predicate_headers_send(session, stream); + } + + if (rv != 0) { + return rv; + } + } + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->headers.nva, frame->headers.nvlen, + NGHTTP2_PRIORITY_SPECLEN); + + if (estimated_payloadlen > session->max_send_header_block_length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, + &session->hd_deflater); + + if (rv != 0) { + return rv; + } + + DEBUGF("send: before padding, HEADERS serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs)); + + rv = session_headers_add_pad(session, frame); + + if (rv != 0) { + return rv; + } + + DEBUGF("send: HEADERS finally serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs)); + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + assert(session->last_sent_stream_id < frame->hd.stream_id); + session->last_sent_stream_id = frame->hd.stream_id; + } + + return 0; + } + case NGHTTP2_PRIORITY: { + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + /* PRIORITY frame can be sent at any time and to any stream + ID. */ + nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); + + /* Peer can send PRIORITY frame against idle stream to create + "anchor" in dependency tree. Only client can do this in + nghttp2. In nghttp2, only server retains non-active (closed + or idle) streams in memory, so we don't open stream here. */ + return 0; + } + case NGHTTP2_RST_STREAM: + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream); + return 0; + case NGHTTP2_SETTINGS: { + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + assert(session->obq_flood_counter_ > 0); + --session->obq_flood_counter_; + /* When session is about to close, don't send SETTINGS ACK. + We are required to send SETTINGS without ACK though; for + example, we have to send SETTINGS as a part of connection + preface. */ + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + } + + rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings); + if (rv != 0) { + return rv; + } + return 0; + } + case NGHTTP2_PUSH_PROMISE: { + nghttp2_stream *stream; + size_t estimated_payloadlen; + + /* stream could be NULL if associated stream was already + closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + /* predicate should fail if stream is NULL. */ + rv = session_predicate_push_promise_send(session, stream); + if (rv != 0) { + return rv; + } + + assert(stream); + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->push_promise.nva, frame->push_promise.nvlen, 0); + + if (estimated_payloadlen > session->max_send_header_block_length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + rv = nghttp2_frame_pack_push_promise( + &session->aob.framebufs, &frame->push_promise, &session->hd_deflater); + if (rv != 0) { + return rv; + } + rv = session_headers_add_pad(session, frame); + if (rv != 0) { + return rv; + } + + assert(session->last_sent_stream_id + 2 <= + frame->push_promise.promised_stream_id); + session->last_sent_stream_id = frame->push_promise.promised_stream_id; + + return 0; + } + case NGHTTP2_PING: + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + assert(session->obq_flood_counter_ > 0); + --session->obq_flood_counter_; + } + /* PING frame is allowed to be sent unless termination GOAWAY is + sent */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); + return 0; + case NGHTTP2_GOAWAY: + rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); + if (rv != 0) { + return rv; + } + session->local_last_stream_id = frame->goaway.last_stream_id; + + return 0; + case NGHTTP2_WINDOW_UPDATE: + rv = session_predicate_window_update_send(session, frame->hd.stream_id); + if (rv != 0) { + return rv; + } + nghttp2_frame_pack_window_update(&session->aob.framebufs, + &frame->window_update); + return 0; + case NGHTTP2_CONTINUATION: + /* We never handle CONTINUATION here. */ + assert(0); + return 0; + default: { + nghttp2_ext_aux_data *aux_data; + + /* extension frame */ + + aux_data = &item->aux_data.ext; + + if (aux_data->builtin == 0) { + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + + return session_pack_extension(session, &session->aob.framebufs, frame); + } + + switch (frame->hd.type) { + case NGHTTP2_ALTSVC: + rv = session_predicate_altsvc_send(session, frame->hd.stream_id); + if (rv != 0) { + return rv; + } + + nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext); + + return 0; + case NGHTTP2_ORIGIN: + rv = session_predicate_origin_send(session); + if (rv != 0) { + return rv; + } + + rv = nghttp2_frame_pack_origin(&session->aob.framebufs, &frame->ext); + if (rv != 0) { + return rv; + } + + return 0; + default: + /* Unreachable here */ + assert(0); + return 0; + } + } + } +} + +nghttp2_outbound_item * +nghttp2_session_get_next_ob_item(nghttp2_session *session) { + if (nghttp2_outbound_queue_top(&session->ob_urgent)) { + return nghttp2_outbound_queue_top(&session->ob_urgent); + } + + if (nghttp2_outbound_queue_top(&session->ob_reg)) { + return nghttp2_outbound_queue_top(&session->ob_reg); + } + + if (!session_is_outgoing_concurrent_streams_max(session)) { + if (nghttp2_outbound_queue_top(&session->ob_syn)) { + return nghttp2_outbound_queue_top(&session->ob_syn); + } + } + + if (session->remote_window_size > 0) { + return nghttp2_stream_next_outbound_item(&session->root); + } + + return NULL; +} + +nghttp2_outbound_item * +nghttp2_session_pop_next_ob_item(nghttp2_session *session) { + nghttp2_outbound_item *item; + + item = nghttp2_outbound_queue_top(&session->ob_urgent); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_urgent); + item->queued = 0; + return item; + } + + item = nghttp2_outbound_queue_top(&session->ob_reg); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_reg); + item->queued = 0; + return item; + } + + if (!session_is_outgoing_concurrent_streams_max(session)) { + item = nghttp2_outbound_queue_top(&session->ob_syn); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_syn); + item->queued = 0; + return item; + } + } + + if (session->remote_window_size > 0) { + return nghttp2_stream_next_outbound_item(&session->root); + } + + return NULL; +} + +static int session_call_before_frame_send(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.before_frame_send_callback) { + rv = session->callbacks.before_frame_send_callback(session, frame, + session->user_data); + if (rv == NGHTTP2_ERR_CANCEL) { + return rv; + } + + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_frame_send(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.on_frame_send_callback) { + rv = session->callbacks.on_frame_send_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int find_stream_on_goaway_func(nghttp2_map_entry *entry, void *ptr) { + nghttp2_close_stream_on_goaway_arg *arg; + nghttp2_stream *stream; + + arg = (nghttp2_close_stream_on_goaway_arg *)ptr; + stream = (nghttp2_stream *)entry; + + if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) { + if (arg->incoming) { + return 0; + } + } else if (!arg->incoming) { + return 0; + } + + if (stream->state != NGHTTP2_STREAM_IDLE && + (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 && + stream->stream_id > arg->last_stream_id) { + /* We are collecting streams to close because we cannot call + nghttp2_session_close_stream() inside nghttp2_map_each(). + Reuse closed_next member.. bad choice? */ + assert(stream->closed_next == NULL); + assert(stream->closed_prev == NULL); + + if (arg->head) { + stream->closed_next = arg->head; + arg->head = stream; + } else { + arg->head = stream; + } + } + + return 0; +} + +/* Closes non-idle and non-closed streams whose stream ID > + last_stream_id. If incoming is nonzero, we are going to close + incoming streams. Otherwise, close outgoing streams. */ +static int session_close_stream_on_goaway(nghttp2_session *session, + int32_t last_stream_id, + int incoming) { + int rv; + nghttp2_stream *stream, *next_stream; + nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id, + incoming}; + + rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg); + assert(rv == 0); + + stream = arg.head; + while (stream) { + next_stream = stream->closed_next; + stream->closed_next = NULL; + rv = nghttp2_session_close_stream(session, stream->stream_id, + NGHTTP2_REFUSED_STREAM); + + /* stream may be deleted here */ + + stream = next_stream; + + if (nghttp2_is_fatal(rv)) { + /* Clean up closed_next member just in case */ + while (stream) { + next_stream = stream->closed_next; + stream->closed_next = NULL; + stream = next_stream; + } + return rv; + } + } + + return 0; +} + +static void reschedule_stream(nghttp2_stream *stream) { + stream->last_writelen = stream->item->frame.hd.length; + + nghttp2_stream_reschedule(stream); +} + +static int session_update_stream_consumed_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size); + +static int session_update_connection_consumed_size(nghttp2_session *session, + size_t delta_size); + +static int session_update_recv_connection_window_size(nghttp2_session *session, + size_t delta_size); + +static int session_update_recv_stream_window_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size, + int send_window_update); + +/* + * Called after a frame is sent. This function runs + * on_frame_send_callback and handles stream closure upon END_STREAM + * or RST_STREAM. This function does not reset session->aob. It is a + * responsibility of session_after_frame_sent2. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +static int session_after_frame_sent1(nghttp2_session *session) { + int rv; + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_bufs *framebufs = &aob->framebufs; + nghttp2_frame *frame; + nghttp2_stream *stream; + + frame = &item->frame; + + if (frame->hd.type == NGHTTP2_DATA) { + nghttp2_data_aux_data *aux_data; + + aux_data = &item->aux_data.data; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + /* We update flow control window after a frame was completely + sent. This is possible because we choose payload length not to + exceed the window */ + session->remote_window_size -= (int32_t)frame->hd.length; + if (stream) { + stream->remote_window_size -= (int32_t)frame->hd.length; + } + + if (stream && aux_data->eof) { + rv = nghttp2_stream_detach_item(stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* Call on_frame_send_callback after + nghttp2_stream_detach_item(), so that application can issue + nghttp2_submit_data() in the callback. */ + if (session->callbacks.on_frame_send_callback) { + rv = session_call_on_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + int stream_closed; + + stream_closed = + (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR; + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* stream may be NULL if it was closed */ + if (stream_closed) { + stream = NULL; + } + } + return 0; + } + + if (session->callbacks.on_frame_send_callback) { + rv = session_call_on_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return 0; + } + + /* non-DATA frame */ + + if (frame->hd.type == NGHTTP2_HEADERS || + frame->hd.type == NGHTTP2_PUSH_PROMISE) { + if (nghttp2_bufs_next_present(framebufs)) { + DEBUGF("send: CONTINUATION exists, just return\n"); + return 0; + } + } + rv = session_call_on_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + nghttp2_headers_aux_data *aux_data; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + return 0; + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: { + stream->state = NGHTTP2_STREAM_OPENING; + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = &item->aux_data.headers; + if (aux_data->data_prd.read_callback) { + /* nghttp2_submit_data() makes a copy of aux_data->data_prd */ + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &aux_data->data_prd); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* TODO nghttp2_submit_data() may fail if stream has already + DATA frame item. We might have to handle it here. */ + } + return 0; + } + case NGHTTP2_HCAT_PUSH_RESPONSE: + stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH); + ++session->num_outgoing_streams; + /* Fall through */ + case NGHTTP2_HCAT_RESPONSE: + stream->state = NGHTTP2_STREAM_OPENED; + /* Fall through */ + case NGHTTP2_HCAT_HEADERS: + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = &item->aux_data.headers; + if (aux_data->data_prd.read_callback) { + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &aux_data->data_prd); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* TODO nghttp2_submit_data() may fail if stream has already + DATA frame item. We might have to handle it here. */ + } + return 0; + default: + /* Unreachable */ + assert(0); + return 0; + } + } + case NGHTTP2_PRIORITY: + if (session->server) { + return 0; + ; + } + + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + + if (!stream) { + if (!session_detect_idle_stream(session, frame->hd.stream_id)) { + return 0; + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_FLAG_NONE, + &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + } else { + rv = nghttp2_session_reprioritize_stream(session, stream, + &frame->priority.pri_spec); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + rv = nghttp2_session_adjust_idle_stream(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; + case NGHTTP2_RST_STREAM: + rv = nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return 0; + case NGHTTP2_GOAWAY: { + nghttp2_goaway_aux_data *aux_data; + + aux_data = &item->aux_data.goaway; + + if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) { + + if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) { + session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT; + } + + session->goaway_flags |= NGHTTP2_GOAWAY_SENT; + + rv = session_close_stream_on_goaway(session, frame->goaway.last_stream_id, + 1); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return 0; + } + case NGHTTP2_WINDOW_UPDATE: + if (frame->hd.stream_id == 0) { + session->window_update_queued = 0; + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + rv = session_update_connection_consumed_size(session, 0); + } else { + rv = session_update_recv_connection_window_size(session, 0); + } + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + return 0; + } + + stream->window_update_queued = 0; + + /* We don't have to send WINDOW_UPDATE if END_STREAM from peer + is seen. */ + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + return 0; + } + + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + rv = session_update_stream_consumed_size(session, stream, 0); + } else { + rv = session_update_recv_stream_window_size(session, stream, 0, 1); + } + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; + default: + return 0; + } +} + +/* + * Called after a frame is sent and session_after_frame_sent1. This + * function is responsible to reset session->aob. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +static int session_after_frame_sent2(nghttp2_session *session) { + int rv; + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_bufs *framebufs = &aob->framebufs; + nghttp2_frame *frame; + nghttp2_mem *mem; + nghttp2_stream *stream; + nghttp2_data_aux_data *aux_data; + + mem = &session->mem; + frame = &item->frame; + + if (frame->hd.type != NGHTTP2_DATA) { + + if (frame->hd.type == NGHTTP2_HEADERS || + frame->hd.type == NGHTTP2_PUSH_PROMISE) { + + if (nghttp2_bufs_next_present(framebufs)) { + framebufs->cur = framebufs->cur->next; + + DEBUGF("send: next CONTINUATION frame, %zu bytes\n", + nghttp2_buf_len(&framebufs->cur->buf)); + + return 0; + } + } + + active_outbound_item_reset(&session->aob, mem); + + return 0; + } + + /* DATA frame */ + + aux_data = &item->aux_data.data; + + /* On EOF, we have already detached data. Please note that + application may issue nghttp2_submit_data() in + on_frame_send_callback (call from session_after_frame_sent1), + which attach data to stream. We don't want to detach it. */ + if (aux_data->eof) { + active_outbound_item_reset(aob, mem); + + return 0; + } + + /* Reset no_copy here because next write may not use this. */ + aux_data->no_copy = 0; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + /* If session is closed or RST_STREAM was queued, we won't send + further data. */ + if (nghttp2_session_predicate_data_send(session, stream) != 0) { + if (stream) { + rv = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + active_outbound_item_reset(aob, mem); + + return 0; + } + + aob->item = NULL; + active_outbound_item_reset(&session->aob, mem); + + return 0; +} + +static int session_call_send_data(nghttp2_session *session, + nghttp2_outbound_item *item, + nghttp2_bufs *framebufs) { + int rv; + nghttp2_buf *buf; + size_t length; + nghttp2_frame *frame; + nghttp2_data_aux_data *aux_data; + + buf = &framebufs->cur->buf; + frame = &item->frame; + length = frame->hd.length - frame->data.padlen; + aux_data = &item->aux_data.data; + + rv = session->callbacks.send_data_callback(session, frame, buf->pos, length, + &aux_data->data_prd.source, + session->user_data); + + switch (rv) { + case 0: + case NGHTTP2_ERR_WOULDBLOCK: + case NGHTTP2_ERR_PAUSE: + case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: + return rv; + default: + return NGHTTP2_ERR_CALLBACK_FAILURE; + } +} + +static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + const uint8_t **data_ptr, + int fast_cb) { + int rv; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_mem *mem; + + mem = &session->mem; + aob = &session->aob; + framebufs = &aob->framebufs; + + /* We may have idle streams more than we expect (e.g., + nghttp2_session_change_stream_priority() or + nghttp2_session_create_idle_stream()). Adjust them here. */ + rv = nghttp2_session_adjust_idle_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + for (;;) { + switch (aob->state) { + case NGHTTP2_OB_POP_ITEM: { + nghttp2_outbound_item *item; + + item = nghttp2_session_pop_next_ob_item(session); + if (item == NULL) { + return 0; + } + + rv = session_prep_frame(session, item); + if (rv == NGHTTP2_ERR_PAUSE) { + return 0; + } + if (rv == NGHTTP2_ERR_DEFERRED) { + DEBUGF("send: frame transmission deferred\n"); + break; + } + if (rv < 0) { + int32_t opened_stream_id = 0; + uint32_t error_code = NGHTTP2_INTERNAL_ERROR; + + DEBUGF("send: frame preparation failed with %s\n", + nghttp2_strerror(rv)); + /* TODO If the error comes from compressor, the connection + must be closed. */ + if (item->frame.hd.type != NGHTTP2_DATA && + session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) { + nghttp2_frame *frame = &item->frame; + /* The library is responsible for the transmission of + WINDOW_UPDATE frame, so we don't call error callback for + it. */ + if (frame->hd.type != NGHTTP2_WINDOW_UPDATE && + session->callbacks.on_frame_not_send_callback( + session, frame, rv, session->user_data) != 0) { + + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + /* We have to close stream opened by failed request HEADERS + or PUSH_PROMISE. */ + switch (item->frame.hd.type) { + case NGHTTP2_HEADERS: + if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { + opened_stream_id = item->frame.hd.stream_id; + if (item->aux_data.headers.canceled) { + error_code = item->aux_data.headers.error_code; + } else { + /* Set error_code to REFUSED_STREAM so that application + can send request again. */ + error_code = NGHTTP2_REFUSED_STREAM; + } + } + break; + case NGHTTP2_PUSH_PROMISE: + opened_stream_id = item->frame.push_promise.promised_stream_id; + break; + } + if (opened_stream_id) { + /* careful not to override rv */ + int rv2; + rv2 = nghttp2_session_close_stream(session, opened_stream_id, + error_code); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + active_outbound_item_reset(aob, mem); + + if (rv == NGHTTP2_ERR_HEADER_COMP) { + /* If header compression error occurred, should terminiate + connection. */ + rv = nghttp2_session_terminate_session(session, + NGHTTP2_INTERNAL_ERROR); + } + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + aob->item = item; + + nghttp2_bufs_rewind(framebufs); + + if (item->frame.hd.type != NGHTTP2_DATA) { + nghttp2_frame *frame; + + frame = &item->frame; + + DEBUGF("send: next frame: payloadlen=%zu, type=%u, flags=0x%02x, " + "stream_id=%d\n", + frame->hd.length, frame->hd.type, frame->hd.flags, + frame->hd.stream_id); + + rv = session_call_before_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv == NGHTTP2_ERR_CANCEL) { + int32_t opened_stream_id = 0; + uint32_t error_code = NGHTTP2_INTERNAL_ERROR; + + if (session->callbacks.on_frame_not_send_callback) { + if (session->callbacks.on_frame_not_send_callback( + session, frame, rv, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + /* We have to close stream opened by canceled request + HEADERS or PUSH_PROMISE. */ + switch (item->frame.hd.type) { + case NGHTTP2_HEADERS: + if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { + opened_stream_id = item->frame.hd.stream_id; + /* We don't have to check + item->aux_data.headers.canceled since it has already + been checked. */ + /* Set error_code to REFUSED_STREAM so that application + can send request again. */ + error_code = NGHTTP2_REFUSED_STREAM; + } + break; + case NGHTTP2_PUSH_PROMISE: + opened_stream_id = item->frame.push_promise.promised_stream_id; + break; + } + if (opened_stream_id) { + /* careful not to override rv */ + int rv2; + rv2 = nghttp2_session_close_stream(session, opened_stream_id, + error_code); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + active_outbound_item_reset(aob, mem); + + break; + } + } else { + DEBUGF("send: next frame: DATA\n"); + + if (item->aux_data.data.no_copy) { + aob->state = NGHTTP2_OB_SEND_NO_COPY; + break; + } + } + + DEBUGF("send: start transmitting frame type=%u, length=%zd\n", + framebufs->cur->buf.pos[3], + framebufs->cur->buf.last - framebufs->cur->buf.pos); + + aob->state = NGHTTP2_OB_SEND_DATA; + + break; + } + case NGHTTP2_OB_SEND_DATA: { + size_t datalen; + nghttp2_buf *buf; + + buf = &framebufs->cur->buf; + + if (buf->pos == buf->last) { + DEBUGF("send: end transmission of a frame\n"); + + /* Frame has completely sent */ + if (fast_cb) { + rv = session_after_frame_sent2(session); + } else { + rv = session_after_frame_sent1(session); + if (rv < 0) { + /* FATAL */ + assert(nghttp2_is_fatal(rv)); + return rv; + } + rv = session_after_frame_sent2(session); + } + if (rv < 0) { + /* FATAL */ + assert(nghttp2_is_fatal(rv)); + return rv; + } + /* We have already adjusted the next state */ + break; + } + + *data_ptr = buf->pos; + datalen = nghttp2_buf_len(buf); + + /* We increment the offset here. If send_callback does not send + everything, we will adjust it. */ + buf->pos += datalen; + + return (ssize_t)datalen; + } + case NGHTTP2_OB_SEND_NO_COPY: { + nghttp2_stream *stream; + nghttp2_frame *frame; + int pause; + + DEBUGF("send: no copy DATA\n"); + + frame = &aob->item->frame; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (stream == NULL) { + DEBUGF("send: no copy DATA cancelled because stream was closed\n"); + + active_outbound_item_reset(aob, mem); + + break; + } + + rv = session_call_send_data(session, aob->item, framebufs); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + active_outbound_item_reset(aob, mem); + + break; + } + + if (rv == NGHTTP2_ERR_WOULDBLOCK) { + return 0; + } + + pause = (rv == NGHTTP2_ERR_PAUSE); + + rv = session_after_frame_sent1(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + rv = session_after_frame_sent2(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + + /* We have already adjusted the next state */ + + if (pause) { + return 0; + } + + break; + } + case NGHTTP2_OB_SEND_CLIENT_MAGIC: { + size_t datalen; + nghttp2_buf *buf; + + buf = &framebufs->cur->buf; + + if (buf->pos == buf->last) { + DEBUGF("send: end transmission of client magic\n"); + active_outbound_item_reset(aob, mem); + break; + } + + *data_ptr = buf->pos; + datalen = nghttp2_buf_len(buf); + + buf->pos += datalen; + + return (ssize_t)datalen; + } + } + } +} + +ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr) { + int rv; + ssize_t len; + + *data_ptr = NULL; + + len = nghttp2_session_mem_send_internal(session, data_ptr, 1); + if (len <= 0) { + return len; + } + + if (session->aob.item) { + /* We have to call session_after_frame_sent1 here to handle stream + closure upon transmission of frames. Otherwise, END_STREAM may + be reached to client before we call nghttp2_session_mem_send + again and we may get exceeding number of incoming streams. */ + rv = session_after_frame_sent1(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return (ssize_t)rv; + } + } + + return len; +} + +int nghttp2_session_send(nghttp2_session *session) { + const uint8_t *data = NULL; + ssize_t datalen; + ssize_t sentlen; + nghttp2_bufs *framebufs; + + framebufs = &session->aob.framebufs; + + for (;;) { + datalen = nghttp2_session_mem_send_internal(session, &data, 0); + if (datalen <= 0) { + return (int)datalen; + } + sentlen = session->callbacks.send_callback(session, data, (size_t)datalen, + 0, session->user_data); + if (sentlen < 0) { + if (sentlen == NGHTTP2_ERR_WOULDBLOCK) { + /* Transmission canceled. Rewind the offset */ + framebufs->cur->buf.pos -= datalen; + + return 0; + } + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + /* Rewind the offset to the amount of unsent bytes */ + framebufs->cur->buf.pos -= datalen - sentlen; + } +} + +static ssize_t session_recv(nghttp2_session *session, uint8_t *buf, + size_t len) { + ssize_t rv; + rv = session->callbacks.recv_callback(session, buf, len, 0, + session->user_data); + if (rv > 0) { + if ((size_t)rv > len) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return rv; +} + +static int session_call_on_begin_frame(nghttp2_session *session, + const nghttp2_frame_hd *hd) { + int rv; + + if (session->callbacks.on_begin_frame_callback) { + + rv = session->callbacks.on_begin_frame_callback(session, hd, + session->user_data); + + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int session_call_on_frame_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.on_frame_recv_callback) { + rv = session->callbacks.on_frame_recv_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_begin_headers(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + DEBUGF("recv: call on_begin_headers callback stream_id=%d\n", + frame->hd.stream_id); + if (session->callbacks.on_begin_headers_callback) { + rv = session->callbacks.on_begin_headers_callback(session, frame, + session->user_data); + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_header(nghttp2_session *session, + const nghttp2_frame *frame, + const nghttp2_hd_nv *nv) { + int rv = 0; + if (session->callbacks.on_header_callback2) { + rv = session->callbacks.on_header_callback2( + session, frame, nv->name, nv->value, nv->flags, session->user_data); + } else if (session->callbacks.on_header_callback) { + rv = session->callbacks.on_header_callback( + session, frame, nv->name->base, nv->name->len, nv->value->base, + nv->value->len, nv->flags, session->user_data); + } + + if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int session_call_on_invalid_header(nghttp2_session *session, + const nghttp2_frame *frame, + const nghttp2_hd_nv *nv) { + int rv; + if (session->callbacks.on_invalid_header_callback2) { + rv = session->callbacks.on_invalid_header_callback2( + session, frame, nv->name, nv->value, nv->flags, session->user_data); + } else if (session->callbacks.on_invalid_header_callback) { + rv = session->callbacks.on_invalid_header_callback( + session, frame, nv->name->base, nv->name->len, nv->value->base, + nv->value->len, nv->flags, session->user_data); + } else { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int +session_call_on_extension_chunk_recv_callback(nghttp2_session *session, + const uint8_t *data, size_t len) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + if (session->callbacks.on_extension_chunk_recv_callback) { + rv = session->callbacks.on_extension_chunk_recv_callback( + session, &frame->hd, data, len, session->user_data); + if (rv == NGHTTP2_ERR_CANCEL) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int session_call_unpack_extension_callback(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + void *payload = NULL; + + rv = session->callbacks.unpack_extension_callback( + session, &payload, &frame->hd, session->user_data); + if (rv == NGHTTP2_ERR_CANCEL) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + frame->ext.payload = payload; + + return 0; +} + +/* + * Handles frame size error. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_handle_frame_size_error(nghttp2_session *session) { + /* TODO Currently no callback is called for this error, because we + call this callback before reading any payload */ + return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR); +} + +static uint32_t get_error_code_from_lib_error_code(int lib_error_code) { + switch (lib_error_code) { + case NGHTTP2_ERR_STREAM_CLOSED: + return NGHTTP2_STREAM_CLOSED; + case NGHTTP2_ERR_HEADER_COMP: + return NGHTTP2_COMPRESSION_ERROR; + case NGHTTP2_ERR_FRAME_SIZE_ERROR: + return NGHTTP2_FRAME_SIZE_ERROR; + case NGHTTP2_ERR_FLOW_CONTROL: + return NGHTTP2_FLOW_CONTROL_ERROR; + case NGHTTP2_ERR_REFUSED_STREAM: + return NGHTTP2_REFUSED_STREAM; + case NGHTTP2_ERR_PROTO: + case NGHTTP2_ERR_HTTP_HEADER: + case NGHTTP2_ERR_HTTP_MESSAGING: + return NGHTTP2_PROTOCOL_ERROR; + default: + return NGHTTP2_INTERNAL_ERROR; + } +} + +/* + * Calls on_invalid_frame_recv_callback if it is set to |session|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * User defined callback function fails. + */ +static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_handle_invalid_stream2(nghttp2_session *session, + int32_t stream_id, + nghttp2_frame *frame, + int lib_error_code) { + int rv; + rv = nghttp2_session_add_rst_stream( + session, stream_id, get_error_code_from_lib_error_code(lib_error_code)); + if (rv != 0) { + return rv; + } + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_handle_invalid_stream(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + return session_handle_invalid_stream2(session, frame->hd.stream_id, frame, + lib_error_code); +} + +static int session_inflate_handle_invalid_stream(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + int rv; + rv = session_handle_invalid_stream(session, frame, lib_error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +/* + * Handles invalid frame which causes connection error. + */ +static int session_handle_invalid_connection(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code, + const char *reason) { + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return nghttp2_session_terminate_session_with_reason( + session, get_error_code_from_lib_error_code(lib_error_code), reason); +} + +static int session_inflate_handle_invalid_connection(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code, + const char *reason) { + int rv; + rv = + session_handle_invalid_connection(session, frame, lib_error_code, reason); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +/* + * Inflates header block in the memory pointed by |in| with |inlen| + * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must + * call this function again, until it returns 0 or one of negative + * error code. If |call_header_cb| is zero, the on_header_callback + * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If + * the given |in| is the last chunk of header block, the |final| must + * be nonzero. If header block is successfully processed (which is + * indicated by the return value 0, NGHTTP2_ERR_PAUSE or + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed + * input bytes is assigned to the |*readlen_ptr|. + * + * This function return 0 if it succeeds, or one of the negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + * The callback returns this error code, indicating that this + * stream should be RST_STREAMed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_PAUSE + * The callback function returned NGHTTP2_ERR_PAUSE + * NGHTTP2_ERR_HEADER_COMP + * Header decompression failed + */ +static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, + size_t *readlen_ptr, uint8_t *in, size_t inlen, + int final, int call_header_cb) { + ssize_t proclen; + int rv; + int inflate_flags; + nghttp2_hd_nv nv; + nghttp2_stream *stream; + nghttp2_stream *subject_stream; + int trailer = 0; + + *readlen_ptr = 0; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + subject_stream = nghttp2_session_get_stream( + session, frame->push_promise.promised_stream_id); + } else { + subject_stream = stream; + trailer = session_trailer_headers(session, stream, frame); + } + + DEBUGF("recv: decoding header block %zu bytes\n", inlen); + for (;;) { + inflate_flags = 0; + proclen = nghttp2_hd_inflate_hd_nv(&session->hd_inflater, &nv, + &inflate_flags, in, inlen, final); + if (nghttp2_is_fatal((int)proclen)) { + return (int)proclen; + } + if (proclen < 0) { + if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) { + if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) { + /* Adding RST_STREAM here is very important. It prevents + from invoking subsequent callbacks for the same stream + ID. */ + rv = nghttp2_session_add_rst_stream( + session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + rv = + nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return NGHTTP2_ERR_HEADER_COMP; + } + in += proclen; + inlen -= (size_t)proclen; + *readlen_ptr += (size_t)proclen; + + DEBUGF("recv: proclen=%zd\n", proclen); + + if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { + rv = 0; + if (subject_stream) { + if (session_enforce_http_messaging(session)) { + rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, + trailer); + + if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) { + /* Don't overwrite rv here */ + int rv2; + + rv2 = session_call_on_invalid_header(session, frame, &nv); + if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = NGHTTP2_ERR_HTTP_HEADER; + } else { + if (rv2 != 0) { + return rv2; + } + + /* header is ignored */ + DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n", + frame->hd.type, frame->hd.stream_id, (int)nv.name->len, + nv.name->base, (int)nv.value->len, nv.value->base); + + rv2 = session_call_error_callback( + session, NGHTTP2_ERR_HTTP_HEADER, + "Ignoring received invalid HTTP header field: frame type: " + "%u, stream: %d, name: [%.*s], value: [%.*s]", + frame->hd.type, frame->hd.stream_id, (int)nv.name->len, + nv.name->base, (int)nv.value->len, nv.value->base); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + } + + if (rv == NGHTTP2_ERR_HTTP_HEADER) { + DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n", + frame->hd.type, frame->hd.stream_id, (int)nv.name->len, + nv.name->base, (int)nv.value->len, nv.value->base); + + rv = session_call_error_callback( + session, NGHTTP2_ERR_HTTP_HEADER, + "Invalid HTTP header field was received: frame type: " + "%u, stream: %d, name: [%.*s], value: [%.*s]", + frame->hd.type, frame->hd.stream_id, (int)nv.name->len, + nv.name->base, (int)nv.value->len, nv.value->base); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = session_handle_invalid_stream2(session, + subject_stream->stream_id, + frame, NGHTTP2_ERR_HTTP_HEADER); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + } + if (rv == 0) { + rv = session_call_on_header(session, frame, &nv); + /* This handles NGHTTP2_ERR_PAUSE and + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */ + if (rv != 0) { + return rv; + } + } + } + } + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(&session->hd_inflater); + break; + } + if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { + break; + } + } + return 0; +} + +/* + * Call this function when HEADERS frame was completely received. + * + * This function returns 0 if it succeeds, or one of negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_end_stream_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return 0; + } + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +static int session_after_header_block_received(nghttp2_session *session) { + int rv = 0; + nghttp2_frame *frame = &session->iframe.frame; + nghttp2_stream *stream; + + /* We don't call on_frame_recv_callback if stream has been closed + already or being closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + + if (session_enforce_http_messaging(session)) { + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + nghttp2_stream *subject_stream; + + subject_stream = nghttp2_session_get_stream( + session, frame->push_promise.promised_stream_id); + if (subject_stream) { + rv = nghttp2_http_on_request_headers(subject_stream, frame); + } + } else { + assert(frame->hd.type == NGHTTP2_HEADERS); + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: + rv = nghttp2_http_on_request_headers(stream, frame); + break; + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + rv = nghttp2_http_on_response_headers(stream); + break; + case NGHTTP2_HCAT_HEADERS: + if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + assert(!session->server); + rv = nghttp2_http_on_response_headers(stream); + } else { + rv = nghttp2_http_on_trailer_headers(stream, frame); + } + break; + default: + assert(0); + } + if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + rv = nghttp2_http_on_remote_end_stream(stream); + } + } + if (rv != 0) { + int32_t stream_id; + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + stream_id = frame->push_promise.promised_stream_id; + } else { + stream_id = frame->hd.stream_id; + } + + rv = session_handle_invalid_stream2(session, stream_id, frame, + NGHTTP2_ERR_HTTP_MESSAGING); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (frame->hd.type == NGHTTP2_HEADERS && + (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + /* Don't call nghttp2_session_close_stream_if_shut_rdwr + because RST_STREAM has been submitted. */ + } + return 0; + } + } + + rv = session_call_on_frame_received(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + return session_end_stream_headers_received(session, frame, stream); +} + +int nghttp2_session_on_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + nghttp2_stream *stream; + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0"); + } + + /* If client receives idle stream from server, it is invalid + regardless stream ID is even or odd. This is because client is + not expected to receive request from server. */ + if (!session->server) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: client received request"); + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + assert(session->server); + + if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) { + if (frame->hd.stream_id == 0 || + nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: invalid stream_id"); + } + + /* RFC 7540 says if an endpoint receives a HEADERS with invalid + * stream ID (e.g, numerically smaller than previous), it MUST + * issue connection error with error code PROTOCOL_ERROR. It is a + * bit hard to detect this, since we cannot remember all streams + * we observed so far. + * + * You might imagine this is really easy. But no. HTTP/2 is + * asynchronous protocol, and usually client and server do not + * share the complete picture of open/closed stream status. For + * example, after server sends RST_STREAM for a stream, client may + * send trailer HEADERS for that stream. If naive server detects + * that, and issued connection error, then it is a bug of server + * implementation since client is not wrong if it did not get + * RST_STREAM when it issued trailer HEADERS. + * + * At the moment, we are very conservative here. We only use + * connection error if stream ID refers idle stream, or we are + * sure that stream is half-closed(remote) or closed. Otherwise + * we just ignore HEADERS for now. + */ + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + session->last_recv_stream_id = frame->hd.stream_id; + + if (session_is_incoming_concurrent_streams_max(session)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: max concurrent streams exceeded"); + } + + if (!session_allow_incoming_new_stream(session)) { + /* We just ignore stream after GOAWAY was sent */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself"); + } + + if (session_is_incoming_concurrent_streams_pending_max(session)) { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_REFUSED_STREAM); + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_session_adjust_closed_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->last_proc_stream_id = session->last_recv_stream_id; + + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + /* This function is only called if stream->state == + NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */ + assert(stream->state == NGHTTP2_STREAM_OPENING && + nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)); + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0"); + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + + We go further, and make it connection error. + */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); + } + stream->state = NGHTTP2_STREAM_OPENED; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv = 0; + assert(stream->state == NGHTTP2_STREAM_RESERVED); + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "push response HEADERS: stream_id == 0"); + } + + if (session->server) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "HEADERS: no HEADERS allowed from client in reserved state"); + } + + if (session_is_incoming_concurrent_streams_max(session)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "push response HEADERS: max concurrent streams exceeded"); + } + + if (!session_allow_incoming_new_stream(session)) { + /* We don't accept new stream after GOAWAY was sent. */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (session_is_incoming_concurrent_streams_pending_max(session)) { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_REFUSED_STREAM); + } + + nghttp2_stream_promise_fulfilled(stream); + if (!nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + --session->num_incoming_reserved_streams; + } + ++session->num_incoming_streams; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv = 0; + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0"); + } + if ((stream->shut_flags & NGHTTP2_SHUT_RD)) { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + + we go further, and make it connection error. + */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); + } + if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + if (stream->state == NGHTTP2_STREAM_OPENED) { + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + /* If this is remote peer initiated stream, it is OK unless it + has sent END_STREAM frame already. But if stream is in + NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race + condition. */ + if (stream->state != NGHTTP2_STREAM_CLOSING) { + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +static int session_process_headers_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + nghttp2_stream *stream; + + rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos); + + if (rv != 0) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack"); + } + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + frame->headers.cat = NGHTTP2_HCAT_REQUEST; + return nghttp2_session_on_request_headers_received(session, frame); + } + + if (stream->state == NGHTTP2_STREAM_RESERVED) { + frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; + return nghttp2_session_on_push_response_headers_received(session, frame, + stream); + } + + if (stream->state == NGHTTP2_STREAM_OPENING && + nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + frame->headers.cat = NGHTTP2_HCAT_RESPONSE; + return nghttp2_session_on_response_headers_received(session, frame, stream); + } + + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + return nghttp2_session_on_headers_received(session, frame, stream); +} + +int nghttp2_session_on_priority_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + + if (frame->hd.stream_id == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "PRIORITY: stream_id == 0"); + } + + if (frame->priority.pri_spec.stream_id == frame->hd.stream_id) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "depend on itself"); + } + + if (!session->server) { + /* Re-prioritization works only in server */ + return session_call_on_frame_received(session, frame); + } + + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + + if (!stream) { + /* PRIORITY against idle stream can create anchor node in + dependency tree. */ + if (!session_detect_idle_stream(session, frame->hd.stream_id)) { + return 0; + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_session_adjust_idle_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } else { + rv = nghttp2_session_reprioritize_stream(session, stream, + &frame->priority.pri_spec); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_adjust_idle_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return session_call_on_frame_received(session, frame); +} + +static int session_process_priority_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos); + + return nghttp2_session_on_priority_received(session, frame); +} + +int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + if (frame->hd.stream_id == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "RST_STREAM: stream_id == 0"); + } + + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "RST_STREAM: stream in idle"); + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (stream) { + /* We may use stream->shut_flags for strict error checking. */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } + + rv = session_call_on_frame_received(session, frame); + if (rv != 0) { + return rv; + } + rv = nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return 0; +} + +static int session_process_rst_stream_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos); + + return nghttp2_session_on_rst_stream_received(session, frame); +} + +static int update_remote_initial_window_size_func(nghttp2_map_entry *entry, + void *ptr) { + int rv; + nghttp2_update_window_size_arg *arg; + nghttp2_stream *stream; + + arg = (nghttp2_update_window_size_arg *)ptr; + stream = (nghttp2_stream *)entry; + + rv = nghttp2_stream_update_remote_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { + return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, + NGHTTP2_FLOW_CONTROL_ERROR); + } + + /* If window size gets positive, push deferred DATA frame to + outbound queue. */ + if (stream->remote_window_size > 0 && + nghttp2_stream_check_deferred_by_flow_control(stream)) { + + rv = nghttp2_stream_resume_deferred_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* + * Updates the remote initial window size of all active streams. If + * error occurs, all streams may not be updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int +session_update_remote_initial_window_size(nghttp2_session *session, + int32_t new_initial_window_size) { + nghttp2_update_window_size_arg arg; + + arg.session = session; + arg.new_window_size = new_initial_window_size; + arg.old_window_size = (int32_t)session->remote_settings.initial_window_size; + + return nghttp2_map_each(&session->streams, + update_remote_initial_window_size_func, &arg); +} + +static int update_local_initial_window_size_func(nghttp2_map_entry *entry, + void *ptr) { + int rv; + nghttp2_update_window_size_arg *arg; + nghttp2_stream *stream; + arg = (nghttp2_update_window_size_arg *)ptr; + stream = (nghttp2_stream *)entry; + rv = nghttp2_stream_update_local_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { + return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, + NGHTTP2_FLOW_CONTROL_ERROR); + } + if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && + stream->window_update_queued == 0 && + nghttp2_should_send_window_update(stream->local_window_size, + stream->recv_window_size)) { + + rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE, + stream->stream_id, + stream->recv_window_size); + if (rv != 0) { + return rv; + } + + stream->recv_window_size = 0; + } + return 0; +} + +/* + * Updates the local initial window size of all active streams. If + * error occurs, all streams may not be updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int +session_update_local_initial_window_size(nghttp2_session *session, + int32_t new_initial_window_size, + int32_t old_initial_window_size) { + nghttp2_update_window_size_arg arg; + arg.session = session; + arg.new_window_size = new_initial_window_size; + arg.old_window_size = old_initial_window_size; + return nghttp2_map_each(&session->streams, + update_local_initial_window_size_func, &arg); +} + +/* + * Apply SETTINGS values |iv| having |niv| elements to the local + * settings. We assumes that all values in |iv| is correct, since we + * validated them in nghttp2_session_add_settings() already. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The header table size is out of range + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_update_local_settings(nghttp2_session *session, + nghttp2_settings_entry *iv, + size_t niv) { + int rv; + size_t i; + int32_t new_initial_window_size = -1; + uint32_t header_table_size = 0; + uint32_t min_header_table_size = UINT32_MAX; + uint8_t header_table_size_seen = 0; + /* For NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, use the value last + seen. For NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, use both minimum + value and last seen value. */ + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + header_table_size_seen = 1; + header_table_size = iv[i].value; + min_header_table_size = nghttp2_min(min_header_table_size, iv[i].value); + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + new_initial_window_size = (int32_t)iv[i].value; + break; + } + } + if (header_table_size_seen) { + if (min_header_table_size < header_table_size) { + rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, + min_header_table_size); + if (rv != 0) { + return rv; + } + } + + rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, + header_table_size); + if (rv != 0) { + return rv; + } + } + if (new_initial_window_size != -1) { + rv = session_update_local_initial_window_size( + session, new_initial_window_size, + (int32_t)session->local_settings.initial_window_size); + if (rv != 0) { + return rv; + } + } + + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + session->local_settings.header_table_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + session->local_settings.enable_push = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + session->local_settings.max_concurrent_streams = iv[i].value; + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + session->local_settings.initial_window_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + session->local_settings.max_frame_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + session->local_settings.max_header_list_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + session->local_settings.enable_connect_protocol = iv[i].value; + break; + } + } + + return 0; +} + +int nghttp2_session_on_settings_received(nghttp2_session *session, + nghttp2_frame *frame, int noack) { + int rv; + size_t i; + nghttp2_mem *mem; + nghttp2_inflight_settings *settings; + + mem = &session->mem; + + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: stream_id != 0"); + } + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + if (frame->settings.niv != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR, + "SETTINGS: ACK and payload != 0"); + } + + settings = session->inflight_settings_head; + + if (!settings) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK"); + } + + rv = nghttp2_session_update_local_settings(session, settings->iv, + settings->niv); + + session->inflight_settings_head = settings->next; + + inflight_settings_del(settings, mem); + + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + return session_handle_invalid_connection(session, frame, rv, NULL); + } + return session_call_on_frame_received(session, frame); + } + + if (!session->remote_settings_received) { + session->remote_settings.max_concurrent_streams = + NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; + session->remote_settings_received = 1; + } + + for (i = 0; i < frame->settings.niv; ++i) { + nghttp2_settings_entry *entry = &frame->settings.iv[i]; + + switch (entry->settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + + rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater, + entry->value); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } else { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_HEADER_COMP, NULL); + } + } + + session->remote_settings.header_table_size = entry->value; + + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + + if (entry->value != 0 && entry->value != 1) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_ENBLE_PUSH"); + } + + if (!session->server && entry->value != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: server attempted to enable push"); + } + + session->remote_settings.enable_push = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + + session->remote_settings.max_concurrent_streams = entry->value; + + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + + /* Update the initial window size of the all active streams */ + /* Check that initial_window_size < (1u << 31) */ + if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FLOW_CONTROL, + "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE"); + } + + rv = session_update_remote_initial_window_size(session, + (int32_t)entry->value); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL); + } + + session->remote_settings.initial_window_size = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + + if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN || + entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE"); + } + + session->remote_settings.max_frame_size = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + + session->remote_settings.max_header_list_size = entry->value; + + break; + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + + if (entry->value != 0 && entry->value != 1) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL"); + } + + if (!session->server && + session->remote_settings.enable_connect_protocol && + entry->value == 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: server attempted to disable " + "SETTINGS_ENABLE_CONNECT_PROTOCOL"); + } + + session->remote_settings.enable_connect_protocol = entry->value; + + break; + } + } + + if (!noack && !session_is_closing(session)) { + rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); + + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return session_handle_invalid_connection(session, frame, + NGHTTP2_ERR_INTERNAL, NULL); + } + } + + return session_call_on_frame_received(session, frame); +} + +static int session_process_settings_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + size_t i; + nghttp2_settings_entry min_header_size_entry; + + if (iframe->max_niv) { + min_header_size_entry = iframe->iv[iframe->max_niv - 1]; + + if (min_header_size_entry.value < UINT32_MAX) { + /* If we have less value, then we must have + SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */ + for (i = 0; i < iframe->niv; ++i) { + if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { + break; + } + } + + assert(i < iframe->niv); + + if (min_header_size_entry.value != iframe->iv[i].value) { + iframe->iv[iframe->niv++] = iframe->iv[i]; + iframe->iv[i] = min_header_size_entry; + } + } + } + + nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv, + iframe->niv); + + iframe->iv = NULL; + iframe->niv = 0; + iframe->max_niv = 0; + + return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */); +} + +int nghttp2_session_on_push_promise_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + nghttp2_stream *promised_stream; + nghttp2_priority_spec pri_spec; + + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0"); + } + if (session->server || session->local_settings.enable_push == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled"); + } + + if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id"); + } + + if (!session_allow_incoming_new_stream(session)) { + /* We just discard PUSH_PROMISE after GOAWAY was sent */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (!session_is_new_peer_stream_id(session, + frame->push_promise.promised_stream_id)) { + /* The spec says if an endpoint receives a PUSH_PROMISE with + illegal stream ID is subject to a connection error of type + PROTOCOL_ERROR. */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "PUSH_PROMISE: invalid promised_stream_id"); + } + + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle"); + } + + session->last_recv_stream_id = frame->push_promise.promised_stream_id; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING || + !session->pending_enable_push || + session->num_incoming_reserved_streams >= + session->max_incoming_reserved_streams) { + /* Currently, client does not retain closed stream, so we don't + check NGHTTP2_SHUT_RD condition here. */ + + rv = nghttp2_session_add_rst_stream( + session, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL); + if (rv != 0) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_STREAM_CLOSED, + "PUSH_PROMISE: stream closed"); + } + + nghttp2_priority_spec_init(&pri_spec, stream->stream_id, + NGHTTP2_DEFAULT_WEIGHT, 0); + + promised_stream = nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_RESERVED, NULL); + + if (!promised_stream) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't call nghttp2_session_adjust_closed_stream(), since we + don't keep closed stream in client side */ + + session->last_proc_stream_id = session->last_recv_stream_id; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +static int session_process_push_promise_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, + iframe->sbuf.pos); + + if (rv != 0) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack"); + } + + return nghttp2_session_on_push_promise_received(session, frame); +} + +int nghttp2_session_on_ping_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "PING: stream_id != 0"); + } + if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 && + (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 && + !session_is_closing(session)) { + /* Peer sent ping, so ping it back */ + rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK, + frame->ping.opaque_data); + if (rv != 0) { + return rv; + } + } + return session_call_on_frame_received(session, frame); +} + +static int session_process_ping_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos); + + return nghttp2_session_on_ping_received(session, frame); +} + +int nghttp2_session_on_goaway_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "GOAWAY: stream_id != 0"); + } + /* Spec says Endpoints MUST NOT increase the value they send in the + last stream identifier. */ + if ((frame->goaway.last_stream_id > 0 && + !nghttp2_session_is_my_stream_id(session, + frame->goaway.last_stream_id)) || + session->remote_last_stream_id < frame->goaway.last_stream_id) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "GOAWAY: invalid last_stream_id"); + } + + session->goaway_flags |= NGHTTP2_GOAWAY_RECV; + + session->remote_last_stream_id = frame->goaway.last_stream_id; + + rv = session_call_on_frame_received(session, frame); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return session_close_stream_on_goaway(session, frame->goaway.last_stream_id, + 0); +} + +static int session_process_goaway_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos, + iframe->lbuf.pos, + nghttp2_buf_len(&iframe->lbuf)); + + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + return nghttp2_session_on_goaway_received(session, frame); +} + +static int +session_on_connection_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + /* Handle connection-level flow control */ + if (frame->window_update.window_size_increment == 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "WINDOW_UPDATE: window_size_increment == 0"); + } + + if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + session->remote_window_size) { + return session_handle_invalid_connection(session, frame, + NGHTTP2_ERR_FLOW_CONTROL, NULL); + } + session->remote_window_size += frame->window_update.window_size_increment; + + return session_call_on_frame_received(session, frame); +} + +static int session_on_stream_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "WINDOW_UPDATE to idle stream"); + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + return 0; + } + if (state_reserved_remote(session, stream)) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream"); + } + if (frame->window_update.window_size_increment == 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "WINDOW_UPDATE: window_size_increment == 0"); + } + if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + stream->remote_window_size) { + return session_handle_invalid_stream(session, frame, + NGHTTP2_ERR_FLOW_CONTROL); + } + stream->remote_window_size += frame->window_update.window_size_increment; + + if (stream->remote_window_size > 0 && + nghttp2_stream_check_deferred_by_flow_control(stream)) { + + rv = nghttp2_stream_resume_deferred_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return session_call_on_frame_received(session, frame); +} + +int nghttp2_session_on_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + if (frame->hd.stream_id == 0) { + return session_on_connection_window_update_received(session, frame); + } else { + return session_on_stream_window_update_received(session, frame); + } +} + +static int session_process_window_update_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_window_update_payload(&frame->window_update, + iframe->sbuf.pos); + + return nghttp2_session_on_window_update_received(session, frame); +} + +int nghttp2_session_on_altsvc_received(nghttp2_session *session, + nghttp2_frame *frame) { + nghttp2_ext_altsvc *altsvc; + nghttp2_stream *stream; + + altsvc = frame->ext.payload; + + /* session->server case has been excluded */ + + if (frame->hd.stream_id == 0) { + if (altsvc->origin_len == 0) { + return session_call_on_invalid_frame_recv_callback(session, frame, + NGHTTP2_ERR_PROTO); + } + } else { + if (altsvc->origin_len > 0) { + return session_call_on_invalid_frame_recv_callback(session, frame, + NGHTTP2_ERR_PROTO); + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + return 0; + } + + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + } + + if (altsvc->field_value_len == 0) { + return session_call_on_invalid_frame_recv_callback(session, frame, + NGHTTP2_ERR_PROTO); + } + + return session_call_on_frame_received(session, frame); +} + +int nghttp2_session_on_origin_received(nghttp2_session *session, + nghttp2_frame *frame) { + return session_call_on_frame_received(session, frame); +} + +static int session_process_altsvc_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_altsvc_payload( + &frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos, + nghttp2_buf_len(&iframe->lbuf)); + + /* nghttp2_frame_unpack_altsvc_payload steals buffer from + iframe->lbuf */ + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + return nghttp2_session_on_altsvc_received(session, frame); +} + +static int session_process_origin_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + nghttp2_mem *mem = &session->mem; + int rv; + + rv = nghttp2_frame_unpack_origin_payload(&frame->ext, iframe->lbuf.pos, + nghttp2_buf_len(&iframe->lbuf), mem); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* Ignore ORIGIN frame which cannot be parsed. */ + return 0; + } + + return nghttp2_session_on_origin_received(session, frame); +} + +static int session_process_extension_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + rv = session_call_unpack_extension_callback(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */ + if (rv != 0) { + return 0; + } + + return session_call_on_frame_received(session, frame); +} + +int nghttp2_session_on_data_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + nghttp2_stream *stream; + + /* We don't call on_frame_recv_callback if stream has been closed + already or being closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { + /* This should be treated as stream error, but it results in lots + of RST_STREAM. So just ignore frame against nonexistent stream + for now. */ + return 0; + } + + if (session_enforce_http_messaging(session) && + (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + if (nghttp2_http_on_remote_end_stream(stream) != 0) { + rv = nghttp2_session_add_rst_stream(session, stream->stream_id, + NGHTTP2_PROTOCOL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + /* Don't call nghttp2_session_close_stream_if_shut_rdwr because + RST_STREAM has been submitted. */ + return 0; + } + } + + rv = session_call_on_frame_received(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* For errors, this function only returns FATAL error. */ +static int session_process_data_frame(nghttp2_session *session) { + int rv; + nghttp2_frame *public_data_frame = &session->iframe.frame; + rv = nghttp2_session_on_data_received(session, public_data_frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return 0; +} + +/* + * Now we have SETTINGS synchronization, flow control error can be + * detected strictly. If DATA frame is received with length > 0 and + * current received window size + delta length is strictly larger than + * local window size, it is subject to FLOW_CONTROL_ERROR, so return + * -1. Note that local_window_size is calculated after SETTINGS ACK is + * received from peer, so peer must honor this limit. If the resulting + * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * return -1 too. + */ +static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta, + int32_t local_window_size) { + if (*recv_window_size_ptr > local_window_size - (int32_t)delta || + *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) { + return -1; + } + *recv_window_size_ptr += (int32_t)delta; + return 0; +} + +/* + * Accumulates received bytes |delta_size| for stream-level flow + * control and decides whether to send WINDOW_UPDATE to that stream. + * If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, WINDOW_UPDATE will not + * be sent. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_update_recv_stream_window_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size, + int send_window_update) { + int rv; + rv = adjust_recv_window_size(&stream->recv_window_size, delta_size, + stream->local_window_size); + if (rv != 0) { + return nghttp2_session_add_rst_stream(session, stream->stream_id, + NGHTTP2_FLOW_CONTROL_ERROR); + } + /* We don't have to send WINDOW_UPDATE if the data received is the + last chunk in the incoming stream. */ + /* We have to use local_settings here because it is the constraint + the remote endpoint should honor. */ + if (send_window_update && + !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && + stream->window_update_queued == 0 && + nghttp2_should_send_window_update(stream->local_window_size, + stream->recv_window_size)) { + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, + stream->stream_id, + stream->recv_window_size); + if (rv != 0) { + return rv; + } + + stream->recv_window_size = 0; + } + return 0; +} + +/* + * Accumulates received bytes |delta_size| for connection-level flow + * control and decides whether to send WINDOW_UPDATE to the + * connection. If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, + * WINDOW_UPDATE will not be sent. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_update_recv_connection_window_size(nghttp2_session *session, + size_t delta_size) { + int rv; + rv = adjust_recv_window_size(&session->recv_window_size, delta_size, + session->local_window_size); + if (rv != 0) { + return nghttp2_session_terminate_session(session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && + session->window_update_queued == 0 && + nghttp2_should_send_window_update(session->local_window_size, + session->recv_window_size)) { + /* Use stream ID 0 to update connection-level flow control + window */ + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0, + session->recv_window_size); + if (rv != 0) { + return rv; + } + + session->recv_window_size = 0; + } + return 0; +} + +static int session_update_consumed_size(nghttp2_session *session, + int32_t *consumed_size_ptr, + int32_t *recv_window_size_ptr, + uint8_t window_update_queued, + int32_t stream_id, size_t delta_size, + int32_t local_window_size) { + int32_t recv_size; + int rv; + + if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) { + return nghttp2_session_terminate_session(session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + + *consumed_size_ptr += (int32_t)delta_size; + + if (window_update_queued == 0) { + /* recv_window_size may be smaller than consumed_size, because it + may be decreased by negative value with + nghttp2_submit_window_update(). */ + recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr); + + if (nghttp2_should_send_window_update(local_window_size, recv_size)) { + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, + stream_id, recv_size); + + if (rv != 0) { + return rv; + } + + *recv_window_size_ptr -= recv_size; + *consumed_size_ptr -= recv_size; + } + } + + return 0; +} + +static int session_update_stream_consumed_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size) { + return session_update_consumed_size( + session, &stream->consumed_size, &stream->recv_window_size, + stream->window_update_queued, stream->stream_id, delta_size, + stream->local_window_size); +} + +static int session_update_connection_consumed_size(nghttp2_session *session, + size_t delta_size) { + return session_update_consumed_size( + session, &session->consumed_size, &session->recv_window_size, + session->window_update_queued, 0, delta_size, session->local_window_size); +} + +/* + * Checks that we can receive the DATA frame for stream, which is + * indicated by |session->iframe.frame.hd.stream_id|. If it is a + * connection error situation, GOAWAY frame will be issued by this + * function. + * + * If the DATA frame is allowed, returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_IGN_PAYLOAD + * The reception of DATA frame is connection error; or should be + * ignored. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_on_data_received_fail_fast(nghttp2_session *session) { + int rv; + nghttp2_stream *stream; + nghttp2_inbound_frame *iframe; + int32_t stream_id; + const char *failure_reason; + uint32_t error_code = NGHTTP2_PROTOCOL_ERROR; + + iframe = &session->iframe; + stream_id = iframe->frame.hd.stream_id; + + if (stream_id == 0) { + /* The spec says that if a DATA frame is received whose stream ID + is 0, the recipient MUST respond with a connection error of + type PROTOCOL_ERROR. */ + failure_reason = "DATA: stream_id == 0"; + goto fail; + } + + if (session_detect_idle_stream(session, stream_id)) { + failure_reason = "DATA: stream in idle"; + error_code = NGHTTP2_PROTOCOL_ERROR; + goto fail; + } + + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + stream = nghttp2_session_get_stream_raw(session, stream_id); + if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) { + failure_reason = "DATA: stream closed"; + error_code = NGHTTP2_STREAM_CLOSED; + goto fail; + } + + return NGHTTP2_ERR_IGN_PAYLOAD; + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + failure_reason = "DATA: stream in half-closed(remote)"; + error_code = NGHTTP2_STREAM_CLOSED; + goto fail; + } + + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_IGN_PAYLOAD; + } + if (stream->state != NGHTTP2_STREAM_OPENED) { + failure_reason = "DATA: stream not opened"; + goto fail; + } + return 0; + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + failure_reason = "DATA: stream in reserved"; + goto fail; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_IGN_PAYLOAD; + } + return 0; +fail: + rv = nghttp2_session_terminate_session_with_reason(session, error_code, + failure_reason); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_PAYLOAD; +} + +static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe, + const uint8_t *in, + const uint8_t *last) { + return nghttp2_min((size_t)(last - in), iframe->payloadleft); +} + +/* + * Resets iframe->sbuf and advance its mark pointer by |left| bytes. + */ +static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) { + nghttp2_buf_reset(&iframe->sbuf); + iframe->sbuf.mark += left; +} + +static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe, + const uint8_t *in, const uint8_t *last) { + size_t readlen; + + readlen = + nghttp2_min((size_t)(last - in), nghttp2_buf_mark_avail(&iframe->sbuf)); + + iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen); + + return readlen; +} + +/* + * Unpacks SETTINGS entry in iframe->sbuf. + */ +static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { + nghttp2_settings_entry iv; + nghttp2_settings_entry *min_header_table_size_entry; + size_t i; + + nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos); + + switch (iv.settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + case NGHTTP2_SETTINGS_ENABLE_PUSH: + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + break; + default: + DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id); + + iframe->iv[iframe->niv++] = iv; + + return; + } + + for (i = 0; i < iframe->niv; ++i) { + if (iframe->iv[i].settings_id == iv.settings_id) { + iframe->iv[i] = iv; + break; + } + } + + if (i == iframe->niv) { + iframe->iv[iframe->niv++] = iv; + } + + if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { + /* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */ + min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1]; + + if (iv.value < min_header_table_size_entry->value) { + min_header_table_size_entry->value = iv.value; + } + } +} + +/* + * Checks PADDED flags and set iframe->sbuf to read them accordingly. + * If padding is set, this function returns 1. If no padding is set, + * this function returns 0. On error, returns -1. + */ +static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe, + nghttp2_frame_hd *hd) { + if (hd->flags & NGHTTP2_FLAG_PADDED) { + if (hd->length < 1) { + return -1; + } + inbound_frame_set_mark(iframe, 1); + return 1; + } + DEBUGF("recv: no padding in payload\n"); + return 0; +} + +/* + * Computes number of padding based on flags. This function returns + * the calculated length if it succeeds, or -1. + */ +static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) { + size_t padlen; + + /* 1 for Pad Length field */ + padlen = (size_t)(iframe->sbuf.pos[0] + 1); + + DEBUGF("recv: padlen=%zu\n", padlen); + + /* We cannot use iframe->frame.hd.length because of CONTINUATION */ + if (padlen - 1 > iframe->payloadleft) { + return -1; + } + + iframe->padlen = padlen; + + return (ssize_t)padlen; +} + +/* + * This function returns the effective payload length in the data of + * length |readlen| when the remaning payload is |payloadleft|. The + * |payloadleft| does not include |readlen|. If padding was started + * strictly before this data chunk, this function returns -1. + */ +static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, + size_t payloadleft, + size_t readlen) { + size_t trail_padlen = + nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); + + if (trail_padlen > payloadleft) { + size_t padlen; + padlen = trail_padlen - payloadleft; + if (readlen < padlen) { + return -1; + } + return (ssize_t)(readlen - padlen); + } + return (ssize_t)(readlen); +} + +ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + size_t inlen) { + const uint8_t *first = in, *last = in + inlen; + nghttp2_inbound_frame *iframe = &session->iframe; + size_t readlen; + ssize_t padlen; + int rv; + int busy = 0; + nghttp2_frame_hd cont_hd; + nghttp2_stream *stream; + size_t pri_fieldlen; + nghttp2_mem *mem; + + DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n", + session->recv_window_size, session->local_window_size); + + mem = &session->mem; + + /* We may have idle streams more than we expect (e.g., + nghttp2_session_change_stream_priority() or + nghttp2_session_create_idle_stream()). Adjust them here. */ + rv = nghttp2_session_adjust_idle_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (!nghttp2_session_want_read(session)) { + return (ssize_t)inlen; + } + + for (;;) { + switch (iframe->state) { + case NGHTTP2_IB_READ_CLIENT_MAGIC: + readlen = nghttp2_min(inlen, iframe->payloadleft); + + if (memcmp(&NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN - + iframe->payloadleft], + in, readlen) != 0) { + return NGHTTP2_ERR_BAD_CLIENT_MAGIC; + } + + iframe->payloadleft -= readlen; + in += readlen; + + if (iframe->payloadleft == 0) { + session_inbound_frame_reset(session); + iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; + } + + break; + case NGHTTP2_IB_READ_FIRST_SETTINGS: + DEBUGF("recv: [IB_READ_FIRST_SETTINGS]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS || + (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) { + rv = session_call_error_callback( + session, NGHTTP2_ERR_SETTINGS_EXPECTED, + "Remote peer returned unexpected data while we expected " + "SETTINGS frame. Perhaps, peer does not support HTTP/2 " + "properly."); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return (ssize_t)inlen; + } + + iframe->state = NGHTTP2_IB_READ_HEAD; + + /* Fall through */ + case NGHTTP2_IB_READ_HEAD: { + int on_begin_frame_called = 0; + + DEBUGF("recv: [IB_READ_HEAD]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos); + iframe->payloadleft = iframe->frame.hd.length; + + DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n", + iframe->frame.hd.length, iframe->frame.hd.type, + iframe->frame.hd.flags, iframe->frame.hd.stream_id); + + if (iframe->frame.hd.length > session->local_settings.max_frame_size) { + DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length, + session->local_settings.max_frame_size); + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return (ssize_t)inlen; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_DATA: { + DEBUGF("recv: DATA\n"); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED); + /* Check stream is open. If it is not open or closing, + ignore payload. */ + busy = 1; + + rv = session_on_data_received_fail_fast(session); + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { + DEBUGF("recv: DATA not allowed stream_id=%d\n", + iframe->frame.hd.stream_id); + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "DATA: insufficient padding space"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_PAD_DATA; + break; + } + + iframe->state = NGHTTP2_IB_READ_DATA; + break; + } + case NGHTTP2_HEADERS: + + DEBUGF("recv: HEADERS\n"); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | + NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY); + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "HEADERS: insufficient padding space"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_NBYTE; + break; + } + + pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); + + if (pri_fieldlen > 0) { + if (iframe->payloadleft < pri_fieldlen) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, pri_fieldlen); + + break; + } + + /* Call on_begin_frame_callback here because + session_process_headers_frame() may call + on_begin_headers_callback */ + rv = session_call_on_begin_frame(session, &iframe->frame.hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + on_begin_frame_called = 1; + + rv = session_process_headers_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PRIORITY: + DEBUGF("recv: PRIORITY\n"); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) { + busy = 1; + + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN); + + break; + case NGHTTP2_RST_STREAM: + case NGHTTP2_WINDOW_UPDATE: +#ifdef DEBUGBUILD + switch (iframe->frame.hd.type) { + case NGHTTP2_RST_STREAM: + DEBUGF("recv: RST_STREAM\n"); + break; + case NGHTTP2_WINDOW_UPDATE: + DEBUGF("recv: WINDOW_UPDATE\n"); + break; + } +#endif /* DEBUGBUILD */ + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft != 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + case NGHTTP2_SETTINGS: + DEBUGF("recv: SETTINGS\n"); + + iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; + + if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) || + ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) && + iframe->payloadleft > 0)) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_SETTINGS; + + if (iframe->payloadleft) { + nghttp2_settings_entry *min_header_table_size_entry; + + /* We allocate iv with additional one entry, to store the + minimum header table size. */ + iframe->max_niv = + iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1; + + iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) * + iframe->max_niv); + + if (!iframe->iv) { + return NGHTTP2_ERR_NOMEM; + } + + min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1]; + min_header_table_size_entry->settings_id = + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + min_header_table_size_entry->value = UINT32_MAX; + + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); + break; + } + + busy = 1; + + inbound_frame_set_mark(iframe, 0); + + break; + case NGHTTP2_PUSH_PROMISE: + DEBUGF("recv: PUSH_PROMISE\n"); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED); + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "PUSH_PROMISE: insufficient padding space"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_NBYTE; + break; + } + + if (iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + case NGHTTP2_PING: + DEBUGF("recv: PING\n"); + + iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; + + if (iframe->payloadleft != 8) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 8); + + break; + case NGHTTP2_GOAWAY: + DEBUGF("recv: GOAWAY\n"); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft < 8) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 8); + + break; + case NGHTTP2_CONTINUATION: + DEBUGF("recv: unexpected CONTINUATION\n"); + + /* Receiving CONTINUATION in this state are subject to + connection error of type PROTOCOL_ERROR */ + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return (ssize_t)inlen; + default: + DEBUGF("recv: extension frame\n"); + + if (check_ext_type_set(session->user_recv_ext_types, + iframe->frame.hd.type)) { + if (!session->callbacks.unpack_extension_callback) { + /* Silently ignore unknown frame type. */ + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD; + + break; + } else { + switch (iframe->frame.hd.type) { + case NGHTTP2_ALTSVC: + if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == + 0) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + DEBUGF("recv: ALTSVC\n"); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc; + + if (session->server) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + if (iframe->payloadleft < 2) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 2); + + break; + case NGHTTP2_ORIGIN: + if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + DEBUGF("recv: ORIGIN\n"); + + iframe->frame.ext.payload = &iframe->ext_frame_payload.origin; + + if (session->server || iframe->frame.hd.stream_id || + (iframe->frame.hd.flags & 0xf0)) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft) { + iframe->raw_lbuf = nghttp2_mem_malloc(mem, iframe->payloadleft); + + if (iframe->raw_lbuf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, + iframe->payloadleft); + } else { + busy = 1; + } + + iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD; + + break; + default: + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + } + } + + if (!on_begin_frame_called) { + switch (iframe->state) { + case NGHTTP2_IB_IGN_HEADER_BLOCK: + case NGHTTP2_IB_IGN_PAYLOAD: + case NGHTTP2_IB_FRAME_SIZE_ERROR: + case NGHTTP2_IB_IGN_DATA: + case NGHTTP2_IB_IGN_ALL: + break; + default: + rv = session_call_on_begin_frame(session, &iframe->frame.hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + break; + } + case NGHTTP2_IB_READ_NBYTE: + DEBUGF("recv: [IB_READ_NBYTE]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + iframe->payloadleft -= readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zd\n", readlen, + iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + if (iframe->padlen == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { + pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0 || + (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + iframe->frame.headers.padlen = (size_t)padlen; + + if (pri_fieldlen > 0) { + if (iframe->payloadleft < pri_fieldlen) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, pri_fieldlen); + break; + } else { + /* Truncate buffers used for padding spec */ + inbound_frame_set_mark(iframe, 0); + } + } + + rv = session_process_headers_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PRIORITY: + rv = session_process_priority_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_RST_STREAM: + rv = session_process_rst_stream_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_PUSH_PROMISE: + if (iframe->padlen == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */ + > 1 + iframe->payloadleft) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "PUSH_PROMISE: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + iframe->frame.push_promise.padlen = (size_t)padlen; + + if (iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + } + + rv = session_process_push_promise_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.push_promise.promised_stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PING: + rv = session_process_ping_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_GOAWAY: { + size_t debuglen; + + /* 8 is Last-stream-ID + Error Code */ + debuglen = iframe->frame.hd.length - 8; + + if (debuglen > 0) { + iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen); + + if (iframe->raw_lbuf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen); + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG; + + break; + } + case NGHTTP2_WINDOW_UPDATE: + rv = session_process_window_update_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_ALTSVC: { + size_t origin_len; + + origin_len = nghttp2_get_uint16(iframe->sbuf.pos); + + DEBUGF("recv: origin_len=%zu\n", origin_len); + + if (origin_len > iframe->payloadleft) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + if (iframe->frame.hd.length > 2) { + iframe->raw_lbuf = + nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2); + + if (iframe->raw_lbuf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, + iframe->frame.hd.length); + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD; + + break; + } + default: + /* This is unknown frame */ + session_inbound_frame_reset(session); + + break; + } + break; + case NGHTTP2_IB_READ_HEADER_BLOCK: + case NGHTTP2_IB_IGN_HEADER_BLOCK: { + ssize_t data_readlen; + size_t trail_padlen; + int final; +#ifdef DEBUGBUILD + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + DEBUGF("recv: [IB_READ_HEADER_BLOCK]\n"); + } else { + DEBUGF("recv: [IB_IGN_HEADER_BLOCK]\n"); + } +#endif /* DEBUGBUILD */ + + readlen = inbound_frame_payload_readlen(iframe, in, last); + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft - readlen); + + data_readlen = inbound_frame_effective_readlen( + iframe, iframe->payloadleft - readlen, readlen); + + if (data_readlen == -1) { + /* everything is padding */ + data_readlen = 0; + } + + trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); + + final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && + iframe->payloadleft - (size_t)data_readlen == trail_padlen; + + if (data_readlen > 0 || (data_readlen == 0 && final)) { + size_t hd_proclen = 0; + + DEBUGF("recv: block final=%d\n", final); + + rv = + inflate_header_block(session, &iframe->frame, &hd_proclen, + (uint8_t *)in, (size_t)data_readlen, final, + iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (rv == NGHTTP2_ERR_PAUSE) { + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + + return in - first; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + /* The application says no more headers. We decompress the + rest of the header block but not invoke on_header_callback + and on_frame_recv_callback. */ + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + + /* Use promised stream ID for PUSH_PROMISE */ + rv = nghttp2_session_add_rst_stream( + session, + iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE + ? iframe->frame.push_promise.promised_stream_id + : iframe->frame.hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + busy = 1; + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + in += readlen; + iframe->payloadleft -= readlen; + + if (rv == NGHTTP2_ERR_HEADER_COMP) { + /* GOAWAY is already issued */ + if (iframe->payloadleft == 0) { + session_inbound_frame_reset(session); + } else { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + } + break; + } + } else { + in += readlen; + iframe->payloadleft -= readlen; + } + + if (iframe->payloadleft) { + break; + } + + if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { + + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN); + + iframe->padlen = 0; + + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION; + } else { + iframe->state = NGHTTP2_IB_IGN_CONTINUATION; + } + } else { + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + rv = session_after_header_block_received(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + session_inbound_frame_reset(session); + } + break; + } + case NGHTTP2_IB_IGN_PAYLOAD: + DEBUGF("recv: [IB_IGN_PAYLOAD]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (iframe->payloadleft) { + break; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + case NGHTTP2_PUSH_PROMISE: + case NGHTTP2_CONTINUATION: + /* Mark inflater bad so that we won't perform further decoding */ + session->hd_inflater.ctx.bad = 1; + break; + default: + break; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_FRAME_SIZE_ERROR: + DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n"); + + rv = session_handle_frame_size_error(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + assert(iframe->state == NGHTTP2_IB_IGN_ALL); + + return (ssize_t)inlen; + case NGHTTP2_IB_READ_SETTINGS: + DEBUGF("recv: [IB_READ_SETTINGS]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + break; + } + + if (readlen > 0) { + inbound_frame_set_settings_entry(iframe); + } + if (iframe->payloadleft) { + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); + break; + } + + rv = session_process_settings_frame(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_READ_GOAWAY_DEBUG: + DEBUGF("recv: [IB_READ_GOAWAY_DEBUG]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + + if (readlen > 0) { + iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); + + iframe->payloadleft -= readlen; + in += readlen; + } + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (iframe->payloadleft) { + assert(nghttp2_buf_avail(&iframe->lbuf) > 0); + + break; + } + + rv = session_process_goaway_frame(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_EXPECT_CONTINUATION: + case NGHTTP2_IB_IGN_CONTINUATION: +#ifdef DEBUGBUILD + if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { + fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n"); + } else { + fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n"); + } +#endif /* DEBUGBUILD */ + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos); + iframe->payloadleft = cont_hd.length; + + DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n", + cont_hd.length, cont_hd.type, cont_hd.flags, cont_hd.stream_id); + + if (cont_hd.type != NGHTTP2_CONTINUATION || + cont_hd.stream_id != iframe->frame.hd.stream_id) { + DEBUGF("recv: expected stream_id=%d, type=%d, but got stream_id=%d, " + "type=%u\n", + iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION, + cont_hd.stream_id, cont_hd.type); + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "unexpected non-CONTINUATION frame or stream_id is invalid"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return (ssize_t)inlen; + } + + /* CONTINUATION won't bear NGHTTP2_PADDED flag */ + + iframe->frame.hd.flags = (uint8_t)( + iframe->frame.hd.flags | (cont_hd.flags & NGHTTP2_FLAG_END_HEADERS)); + iframe->frame.hd.length += cont_hd.length; + + busy = 1; + + if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + rv = session_call_on_begin_frame(session, &cont_hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } else { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + } + + break; + case NGHTTP2_IB_READ_PAD_DATA: + DEBUGF("recv: [IB_READ_PAD_DATA]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + iframe->payloadleft -= readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen, + iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + /* Pad Length field is subject to flow control */ + rv = session_update_recv_connection_window_size(session, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + /* Pad Length field is consumed immediately */ + rv = + nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); + if (stream) { + rv = session_update_recv_stream_window_size( + session, stream, readlen, + iframe->payloadleft || + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + busy = 1; + + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + iframe->frame.data.padlen = (size_t)padlen; + + iframe->state = NGHTTP2_IB_READ_DATA; + + break; + case NGHTTP2_IB_READ_DATA: + stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); + + if (!stream) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + + DEBUGF("recv: [IB_READ_DATA]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (readlen > 0) { + ssize_t data_readlen; + + rv = session_update_recv_connection_window_size(session, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + rv = session_update_recv_stream_window_size( + session, stream, readlen, + iframe->payloadleft || + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + data_readlen = inbound_frame_effective_readlen( + iframe, iframe->payloadleft, readlen); + + if (data_readlen == -1) { + /* everything is padding */ + data_readlen = 0; + } + + padlen = (ssize_t)readlen - data_readlen; + + if (padlen > 0) { + /* Padding is considered as "consumed" immediately */ + rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, + (size_t)padlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + } + + DEBUGF("recv: data_readlen=%zd\n", data_readlen); + + if (data_readlen > 0) { + if (session_enforce_http_messaging(session)) { + if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) { + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + /* Consume all data for connection immediately here */ + rv = session_update_connection_consumed_size( + session, (size_t)data_readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_DATA) { + return (ssize_t)inlen; + } + } + + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + busy = 1; + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + } + if (session->callbacks.on_data_chunk_recv_callback) { + rv = session->callbacks.on_data_chunk_recv_callback( + session, iframe->frame.hd.flags, iframe->frame.hd.stream_id, + in - readlen, (size_t)data_readlen, session->user_data); + if (rv == NGHTTP2_ERR_PAUSE) { + return in - first; + } + + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + } + } + + if (iframe->payloadleft) { + break; + } + + rv = session_process_data_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_IGN_DATA: + DEBUGF("recv: [IB_IGN_DATA]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (readlen > 0) { + /* Update connection-level flow control window for ignored + DATA frame too */ + rv = session_update_recv_connection_window_size(session, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + + /* Ignored DATA is considered as "consumed" immediately. */ + rv = session_update_connection_consumed_size(session, readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + } + } + + if (iframe->payloadleft) { + break; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_IGN_ALL: + return (ssize_t)inlen; + case NGHTTP2_IB_READ_EXTENSION_PAYLOAD: + DEBUGF("recv: [IB_READ_EXTENSION_PAYLOAD]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (readlen > 0) { + rv = session_call_on_extension_chunk_recv_callback( + session, in - readlen, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv != 0) { + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + } + + if (iframe->payloadleft > 0) { + break; + } + + rv = session_process_extension_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_READ_ALTSVC_PAYLOAD: + DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + if (readlen > 0) { + iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); + + iframe->payloadleft -= readlen; + in += readlen; + } + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (iframe->payloadleft) { + assert(nghttp2_buf_avail(&iframe->lbuf) > 0); + + break; + } + + rv = session_process_altsvc_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_READ_ORIGIN_PAYLOAD: + DEBUGF("recv: [IB_READ_ORIGIN_PAYLOAD]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + + if (readlen > 0) { + iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); + + iframe->payloadleft -= readlen; + in += readlen; + } + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (iframe->payloadleft) { + assert(nghttp2_buf_avail(&iframe->lbuf) > 0); + + break; + } + + rv = session_process_origin_frame(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + } + + if (!busy && in == last) { + break; + } + + busy = 0; + } + + assert(in == last); + + return in - first; +} + +int nghttp2_session_recv(nghttp2_session *session) { + uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH]; + while (1) { + ssize_t readlen; + readlen = session_recv(session, buf, sizeof(buf)); + if (readlen > 0) { + ssize_t proclen = nghttp2_session_mem_recv(session, buf, (size_t)readlen); + if (proclen < 0) { + return (int)proclen; + } + assert(proclen == readlen); + } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) { + return 0; + } else if (readlen == NGHTTP2_ERR_EOF) { + return NGHTTP2_ERR_EOF; + } else if (readlen < 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } +} + +/* + * Returns the number of active streams, which includes streams in + * reserved state. + */ +static size_t session_get_num_active_streams(nghttp2_session *session) { + return nghttp2_map_size(&session->streams) - session->num_closed_streams - + session->num_idle_streams; +} + +int nghttp2_session_want_read(nghttp2_session *session) { + size_t num_active_streams; + + /* If this flag is set, we don't want to read. The application + should drop the connection. */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { + return 0; + } + + num_active_streams = session_get_num_active_streams(session); + + /* Unless termination GOAWAY is sent or received, we always want to + read incoming frames. */ + + if (num_active_streams > 0) { + return 1; + } + + /* If there is no active streams and GOAWAY has been sent or + received, we are done with this session. */ + return (session->goaway_flags & + (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; +} + +int nghttp2_session_want_write(nghttp2_session *session) { + /* If these flag is set, we don't want to write any data. The + application should drop the connection. */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { + return 0; + } + + /* + * Unless termination GOAWAY is sent or received, we want to write + * frames if there is pending ones. If pending frame is request/push + * response HEADERS and concurrent stream limit is reached, we don't + * want to write them. + */ + return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) || + nghttp2_outbound_queue_top(&session->ob_reg) || + (!nghttp2_pq_empty(&session->root.obq) && + session->remote_window_size > 0) || + (nghttp2_outbound_queue_top(&session->ob_syn) && + !session_is_outgoing_concurrent_streams_max(session)); +} + +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + + if ((flags & NGHTTP2_FLAG_ACK) && + session->obq_flood_counter_ >= session->max_outbound_ack) { + return NGHTTP2_ERR_FLOODED; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_ping_init(&frame->ping, flags, opaque_data); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_ping_free(&frame->ping); + nghttp2_mem_free(mem, item); + return rv; + } + + if (flags & NGHTTP2_FLAG_ACK) { + ++session->obq_flood_counter_; + } + + return 0; +} + +int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, + uint32_t error_code, const uint8_t *opaque_data, + size_t opaque_data_len, uint8_t aux_flags) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + uint8_t *opaque_data_copy = NULL; + nghttp2_goaway_aux_data *aux_data; + nghttp2_mem *mem; + + mem = &session->mem; + + if (nghttp2_session_is_my_stream_id(session, last_stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (opaque_data_len) { + if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len); + if (opaque_data_copy == NULL) { + return NGHTTP2_ERR_NOMEM; + } + memcpy(opaque_data_copy, opaque_data, opaque_data_len); + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + nghttp2_mem_free(mem, opaque_data_copy); + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + /* last_stream_id must not be increased from the value previously + sent */ + last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id); + + nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code, + opaque_data_copy, opaque_data_len); + + aux_data = &item->aux_data.goaway; + aux_data->flags = aux_flags; + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_goaway_free(&frame->goaway, mem); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id, + window_size_increment); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_window_update_free(&frame->window_update); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +static void +session_append_inflight_settings(nghttp2_session *session, + nghttp2_inflight_settings *settings) { + nghttp2_inflight_settings **i; + + for (i = &session->inflight_settings_head; *i; i = &(*i)->next) + ; + + *i = settings; +} + +int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv) { + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_settings_entry *iv_copy; + size_t i; + int rv; + nghttp2_mem *mem; + nghttp2_inflight_settings *inflight_settings = NULL; + + mem = &session->mem; + + if (flags & NGHTTP2_FLAG_ACK) { + if (niv != 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (session->obq_flood_counter_ >= session->max_outbound_ack) { + return NGHTTP2_ERR_FLOODED; + } + } + + if (!nghttp2_iv_check(iv, niv)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + if (niv > 0) { + iv_copy = nghttp2_frame_iv_copy(iv, niv, mem); + if (iv_copy == NULL) { + nghttp2_mem_free(mem, item); + return NGHTTP2_ERR_NOMEM; + } + } else { + iv_copy = NULL; + } + + if ((flags & NGHTTP2_FLAG_ACK) == 0) { + rv = inflight_settings_new(&inflight_settings, iv, niv, mem); + if (rv != 0) { + assert(nghttp2_is_fatal(rv)); + nghttp2_mem_free(mem, iv_copy); + nghttp2_mem_free(mem, item); + return rv; + } + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv); + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + /* The only expected error is fatal one */ + assert(nghttp2_is_fatal(rv)); + + inflight_settings_del(inflight_settings, mem); + + nghttp2_frame_settings_free(&frame->settings, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + if (flags & NGHTTP2_FLAG_ACK) { + ++session->obq_flood_counter_; + } else { + session_append_inflight_settings(session, inflight_settings); + } + + /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH + here. We use it to refuse the incoming stream and PUSH_PROMISE + with RST_STREAM. */ + + for (i = niv; i > 0; --i) { + if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) { + session->pending_local_max_concurrent_stream = iv[i - 1].value; + break; + } + } + + for (i = niv; i > 0; --i) { + if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) { + session->pending_enable_push = (uint8_t)iv[i - 1].value; + break; + } + } + + for (i = niv; i > 0; --i) { + if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) { + session->pending_enable_connect_protocol = (uint8_t)iv[i - 1].value; + break; + } + } + + return 0; +} + +int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, + size_t datamax, nghttp2_frame *frame, + nghttp2_data_aux_data *aux_data, + nghttp2_stream *stream) { + int rv; + uint32_t data_flags; + ssize_t payloadlen; + ssize_t padded_payloadlen; + nghttp2_buf *buf; + size_t max_payloadlen; + + assert(bufs->head == bufs->cur); + + buf = &bufs->cur->buf; + + if (session->callbacks.read_length_callback) { + + payloadlen = session->callbacks.read_length_callback( + session, frame->hd.type, stream->stream_id, session->remote_window_size, + stream->remote_window_size, session->remote_settings.max_frame_size, + session->user_data); + + DEBUGF("send: read_length_callback=%zd\n", payloadlen); + + payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream, + payloadlen); + + DEBUGF("send: read_length_callback after flow control=%zd\n", payloadlen); + + if (payloadlen <= 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + if ((size_t)payloadlen > nghttp2_buf_avail(buf)) { + /* Resize the current buffer(s). The reason why we do +1 for + buffer size is for possible padding field. */ + rv = nghttp2_bufs_realloc(&session->aob.framebufs, + (size_t)(NGHTTP2_FRAME_HDLEN + 1 + payloadlen)); + + if (rv != 0) { + DEBUGF("send: realloc buffer failed rv=%d", rv); + /* If reallocation failed, old buffers are still in tact. So + use safe limit. */ + payloadlen = (ssize_t)datamax; + + DEBUGF("send: use safe limit payloadlen=%zd", payloadlen); + } else { + assert(&session->aob.framebufs == bufs); + + buf = &bufs->cur->buf; + } + } + datamax = (size_t)payloadlen; + } + + /* Current max DATA length is less then buffer chunk size */ + assert(nghttp2_buf_avail(buf) >= datamax); + + data_flags = NGHTTP2_DATA_FLAG_NONE; + payloadlen = aux_data->data_prd.read_callback( + session, frame->hd.stream_id, buf->pos, datamax, &data_flags, + &aux_data->data_prd.source, session->user_data); + + if (payloadlen == NGHTTP2_ERR_DEFERRED || + payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE || + payloadlen == NGHTTP2_ERR_PAUSE) { + DEBUGF("send: DATA postponed due to %s\n", + nghttp2_strerror((int)payloadlen)); + + return (int)payloadlen; + } + + if (payloadlen < 0 || datamax < (size_t)payloadlen) { + /* This is the error code when callback is failed. */ + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + buf->last = buf->pos + payloadlen; + buf->pos -= NGHTTP2_FRAME_HDLEN; + + /* Clear flags, because this may contain previous flags of previous + DATA */ + frame->hd.flags = NGHTTP2_FLAG_NONE; + + if (data_flags & NGHTTP2_DATA_FLAG_EOF) { + aux_data->eof = 1; + /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set + NGHTTP2_FLAG_END_STREAM */ + if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) && + (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) { + frame->hd.flags |= NGHTTP2_FLAG_END_STREAM; + } + } + + if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) { + if (session->callbacks.send_data_callback == NULL) { + DEBUGF("NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n"); + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + aux_data->no_copy = 1; + } + + frame->hd.length = (size_t)payloadlen; + frame->data.padlen = 0; + + max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN); + + padded_payloadlen = + session_call_select_padding(session, frame, max_payloadlen); + + if (nghttp2_is_fatal((int)padded_payloadlen)) { + return (int)padded_payloadlen; + } + + frame->data.padlen = (size_t)(padded_payloadlen - payloadlen); + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen, + aux_data->no_copy); + if (rv != 0) { + return rv; + } + + reschedule_stream(stream); + + if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) && + (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) { + /* DATA payload length is 0, and DATA frame does not bear + END_STREAM. In this case, there is no point to send 0 length + DATA frame. */ + return NGHTTP2_ERR_CANCEL; + } + + return 0; +} + +void *nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream) { + return stream->stream_user_data; + } else { + return NULL; + } +} + +int nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, + void *stream_user_data) { + nghttp2_stream *stream; + nghttp2_frame *frame; + nghttp2_outbound_item *item; + + stream = nghttp2_session_get_stream(session, stream_id); + if (stream) { + stream->stream_user_data = stream_user_data; + return 0; + } + + if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) || + !nghttp2_outbound_queue_top(&session->ob_syn)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; + assert(frame->hd.type == NGHTTP2_HEADERS); + + if (frame->hd.stream_id > stream_id || + (uint32_t)stream_id >= session->next_stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + for (item = session->ob_syn.head; item; item = item->qnext) { + if (item->frame.hd.stream_id < stream_id) { + continue; + } + + if (item->frame.hd.stream_id > stream_id) { + break; + } + + item->aux_data.headers.stream_user_data = stream_user_data; + return 0; + } + + return NGHTTP2_ERR_INVALID_ARGUMENT; +} + +int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { + int rv; + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = nghttp2_stream_resume_deferred_item(stream, + NGHTTP2_STREAM_FLAG_DEFERRED_USER); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) { + return nghttp2_outbound_queue_size(&session->ob_urgent) + + nghttp2_outbound_queue_size(&session->ob_reg) + + nghttp2_outbound_queue_size(&session->ob_syn); + /* TODO account for item attached to stream */ +} + +int32_t +nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + return stream->recv_window_size < 0 ? 0 : stream->recv_window_size; +} + +int32_t +nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + return stream->local_window_size; +} + +int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + int32_t size; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + + size = stream->local_window_size - stream->recv_window_size; + + /* size could be negative if local endpoint reduced + SETTINGS_INITIAL_WINDOW_SIZE */ + if (size < 0) { + return 0; + } + + return size; +} + +int32_t +nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) { + return session->recv_window_size < 0 ? 0 : session->recv_window_size; +} + +int32_t +nghttp2_session_get_effective_local_window_size(nghttp2_session *session) { + return session->local_window_size; +} + +int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) { + return session->local_window_size - session->recv_window_size; +} + +int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + + /* stream->remote_window_size can be negative when + SETTINGS_INITIAL_WINDOW_SIZE is changed. */ + return nghttp2_max(0, stream->remote_window_size); +} + +int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) { + return session->remote_window_size; +} + +uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, + nghttp2_settings_id id) { + switch (id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + return session->remote_settings.header_table_size; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + return session->remote_settings.enable_push; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return session->remote_settings.max_concurrent_streams; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + return session->remote_settings.initial_window_size; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + return session->remote_settings.max_frame_size; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return session->remote_settings.max_header_list_size; + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + return session->remote_settings.enable_connect_protocol; + } + + assert(0); + abort(); /* if NDEBUG is set */ +} + +uint32_t nghttp2_session_get_local_settings(nghttp2_session *session, + nghttp2_settings_id id) { + switch (id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + return session->local_settings.header_table_size; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + return session->local_settings.enable_push; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return session->local_settings.max_concurrent_streams; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + return session->local_settings.initial_window_size; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + return session->local_settings.max_frame_size; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return session->local_settings.max_header_list_size; + case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: + return session->local_settings.enable_connect_protocol; + } + + assert(0); + abort(); /* if NDEBUG is set */ +} + +static int nghttp2_session_upgrade_internal(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data) { + nghttp2_stream *stream; + nghttp2_frame frame; + nghttp2_settings_entry *iv; + size_t niv; + int rv; + nghttp2_priority_spec pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if ((!session->server && session->next_stream_id != 1) || + (session->server && session->last_recv_stream_id >= 1)) { + return NGHTTP2_ERR_PROTO; + } + if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload, + settings_payloadlen, mem); + if (rv != 0) { + return rv; + } + + if (session->server) { + nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS, + NGHTTP2_FLAG_NONE, 0); + frame.settings.iv = iv; + frame.settings.niv = niv; + rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */); + } else { + rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv); + } + nghttp2_mem_free(mem, iv); + if (rv != 0) { + return rv; + } + + nghttp2_priority_spec_default_init(&pri_spec); + + stream = nghttp2_session_open_stream( + session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING, + session->server ? NULL : stream_user_data); + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't call nghttp2_session_adjust_closed_stream(), since this + should be the first stream open. */ + + if (session->server) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + session->last_recv_stream_id = 1; + session->last_proc_stream_id = 1; + } else { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + session->last_sent_stream_id = 1; + session->next_stream_id += 2; + } + return 0; +} + +int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data) { + int rv; + nghttp2_stream *stream; + + rv = nghttp2_session_upgrade_internal(session, settings_payload, + settings_payloadlen, stream_user_data); + if (rv != 0) { + return rv; + } + + stream = nghttp2_session_get_stream(session, 1); + assert(stream); + + /* We have no information about request header fields when Upgrade + was happened. So we don't know the request method here. If + request method is HEAD, we have a trouble because we may have + nonzero content-length header field in response headers, and we + will going to check it against the actual DATA frames, but we may + get mismatch because HEAD response body must be empty. Because + of this reason, nghttp2_session_upgrade() was deprecated in favor + of nghttp2_session_upgrade2(), which has |head_request| parameter + to indicate that request method is HEAD or not. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND; + return 0; +} + +int nghttp2_session_upgrade2(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, int head_request, + void *stream_user_data) { + int rv; + nghttp2_stream *stream; + + rv = nghttp2_session_upgrade_internal(session, settings_payload, + settings_payloadlen, stream_user_data); + if (rv != 0) { + return rv; + } + + stream = nghttp2_session_get_stream(session, 1); + assert(stream); + + if (head_request) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + } + + return 0; +} + +int nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return -1; + } + + return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0; +} + +int nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return -1; + } + + return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0; +} + +int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, + size_t size) { + int rv; + nghttp2_stream *stream; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + rv = session_update_connection_consumed_size(session, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return 0; + } + + rv = session_update_stream_consumed_size(session, stream, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) { + int rv; + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + rv = session_update_connection_consumed_size(session, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, + size_t size) { + int rv; + nghttp2_stream *stream; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return 0; + } + + rv = session_update_stream_consumed_size(session, stream, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id) { + if (next_stream_id <= 0 || + session->next_stream_id > (uint32_t)next_stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (session->server) { + if (next_stream_id % 2) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (next_stream_id % 2 == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + session->next_stream_id = (uint32_t)next_stream_id; + return 0; +} + +uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) { + return session->next_stream_id; +} + +int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) { + return session->last_proc_stream_id; +} + +nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session, + int32_t stream_id) { + if (stream_id == 0) { + return &session->root; + } + + return nghttp2_session_get_stream_raw(session, stream_id); +} + +nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) { + return &session->root; +} + +int nghttp2_session_check_server_session(nghttp2_session *session) { + return session->server; +} + +int nghttp2_session_change_stream_priority( + nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + int rv; + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec_copy; + + if (stream_id == 0 || stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + stream = nghttp2_session_get_stream_raw(session, stream_id); + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + pri_spec_copy = *pri_spec; + nghttp2_priority_spec_normalize_weight(&pri_spec_copy); + + rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec_copy); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* We don't intentionally call nghttp2_session_adjust_idle_stream() + so that idle stream created by this function, and existing ones + are kept for application. We will adjust number of idle stream + in nghttp2_session_mem_send or nghttp2_session_mem_recv is + called. */ + return 0; +} + +int nghttp2_session_create_idle_stream(nghttp2_session *session, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec_copy; + + if (stream_id == 0 || stream_id == pri_spec->stream_id || + !session_detect_idle_stream(session, stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + stream = nghttp2_session_get_stream_raw(session, stream_id); + if (stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + pri_spec_copy = *pri_spec; + nghttp2_priority_spec_normalize_weight(&pri_spec_copy); + + stream = + nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_copy, NGHTTP2_STREAM_IDLE, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't intentionally call nghttp2_session_adjust_idle_stream() + so that idle stream created by this function, and existing ones + are kept for application. We will adjust number of idle stream + in nghttp2_session_mem_send or nghttp2_session_mem_recv is + called. */ + return 0; +} + +size_t +nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session) { + return nghttp2_hd_inflate_get_dynamic_table_size(&session->hd_inflater); +} + +size_t +nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) { + return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater); +} + +void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) { + session->user_data = user_data; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_session.h b/Utilities/cmnghttp2/lib/nghttp2_session.h new file mode 100644 index 00000000000..90ead9c0395 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_session.h @@ -0,0 +1,901 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_SESSION_H +#define NGHTTP2_SESSION_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_map.h" +#include "nghttp2_frame.h" +#include "nghttp2_hd.h" +#include "nghttp2_stream.h" +#include "nghttp2_outbound_item.h" +#include "nghttp2_int.h" +#include "nghttp2_buf.h" +#include "nghttp2_callbacks.h" +#include "nghttp2_mem.h" + +/* The global variable for tests where we want to disable strict + preface handling. */ +extern int nghttp2_enable_strict_preface; + +/* + * Option flags. + */ +typedef enum { + NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0, + NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1, + NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2, + NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3, + NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4 +} nghttp2_optmask; + +/* + * bitmask for built-in type to enable the default handling for that + * type of the frame. + */ +typedef enum { + NGHTTP2_TYPEMASK_NONE = 0, + NGHTTP2_TYPEMASK_ALTSVC = 1 << 0, + NGHTTP2_TYPEMASK_ORIGIN = 1 << 1 +} nghttp2_typemask; + +typedef enum { + NGHTTP2_OB_POP_ITEM, + NGHTTP2_OB_SEND_DATA, + NGHTTP2_OB_SEND_NO_COPY, + NGHTTP2_OB_SEND_CLIENT_MAGIC +} nghttp2_outbound_state; + +typedef struct { + nghttp2_outbound_item *item; + nghttp2_bufs framebufs; + nghttp2_outbound_state state; +} nghttp2_active_outbound_item; + +/* Buffer length for inbound raw byte stream used in + nghttp2_session_recv(). */ +#define NGHTTP2_INBOUND_BUFFER_LENGTH 16384 + +/* The default maximum number of incoming reserved streams */ +#define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200 + +/* Even if we have less SETTINGS_MAX_CONCURRENT_STREAMS than this + number, we keep NGHTTP2_MIN_IDLE_STREAMS streams in idle state */ +#define NGHTTP2_MIN_IDLE_STREAMS 16 + +/* The maximum number of items in outbound queue, which is considered + as flooding caused by peer. All frames are not considered here. + We only consider PING + ACK and SETTINGS + ACK. This is because + they both are response to the frame initiated by peer and peer can + send as many of them as they want. If peer does not read network, + response frames are stacked up, which leads to memory exhaustion. + The value selected here is arbitrary, but safe value and if we have + these frames in this number, it is considered suspicious. */ +#define NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM 1000 + +/* The default value of maximum number of concurrent streams. */ +#define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu + +/* Internal state when receiving incoming frame */ +typedef enum { + /* Receiving frame header */ + NGHTTP2_IB_READ_CLIENT_MAGIC, + NGHTTP2_IB_READ_FIRST_SETTINGS, + NGHTTP2_IB_READ_HEAD, + NGHTTP2_IB_READ_NBYTE, + NGHTTP2_IB_READ_HEADER_BLOCK, + NGHTTP2_IB_IGN_HEADER_BLOCK, + NGHTTP2_IB_IGN_PAYLOAD, + NGHTTP2_IB_FRAME_SIZE_ERROR, + NGHTTP2_IB_READ_SETTINGS, + NGHTTP2_IB_READ_GOAWAY_DEBUG, + NGHTTP2_IB_EXPECT_CONTINUATION, + NGHTTP2_IB_IGN_CONTINUATION, + NGHTTP2_IB_READ_PAD_DATA, + NGHTTP2_IB_READ_DATA, + NGHTTP2_IB_IGN_DATA, + NGHTTP2_IB_IGN_ALL, + NGHTTP2_IB_READ_ALTSVC_PAYLOAD, + NGHTTP2_IB_READ_ORIGIN_PAYLOAD, + NGHTTP2_IB_READ_EXTENSION_PAYLOAD +} nghttp2_inbound_state; + +typedef struct { + nghttp2_frame frame; + /* Storage for extension frame payload. frame->ext.payload points + to this structure to avoid frequent memory allocation. */ + nghttp2_ext_frame_payload ext_frame_payload; + /* The received SETTINGS entry. For the standard settings entries, + we only keep the last seen value. For + SETTINGS_HEADER_TABLE_SIZE, we also keep minimum value in the + last index. */ + nghttp2_settings_entry *iv; + /* buffer pointers to small buffer, raw_sbuf */ + nghttp2_buf sbuf; + /* buffer pointers to large buffer, raw_lbuf */ + nghttp2_buf lbuf; + /* Large buffer, malloced on demand */ + uint8_t *raw_lbuf; + /* The number of entry filled in |iv| */ + size_t niv; + /* The number of entries |iv| can store. */ + size_t max_niv; + /* How many bytes we still need to receive for current frame */ + size_t payloadleft; + /* padding length for the current frame */ + size_t padlen; + nghttp2_inbound_state state; + /* Small buffer. Currently the largest contiguous chunk to buffer + is frame header. We buffer part of payload, but they are smaller + than frame header. */ + uint8_t raw_sbuf[NGHTTP2_FRAME_HDLEN]; +} nghttp2_inbound_frame; + +typedef struct { + uint32_t header_table_size; + uint32_t enable_push; + uint32_t max_concurrent_streams; + uint32_t initial_window_size; + uint32_t max_frame_size; + uint32_t max_header_list_size; + uint32_t enable_connect_protocol; +} nghttp2_settings_storage; + +typedef enum { + NGHTTP2_GOAWAY_NONE = 0, + /* Flag means that connection should be terminated after sending GOAWAY. */ + NGHTTP2_GOAWAY_TERM_ON_SEND = 0x1, + /* Flag means GOAWAY to terminate session has been sent */ + NGHTTP2_GOAWAY_TERM_SENT = 0x2, + /* Flag means GOAWAY was sent */ + NGHTTP2_GOAWAY_SENT = 0x4, + /* Flag means GOAWAY was received */ + NGHTTP2_GOAWAY_RECV = 0x8 +} nghttp2_goaway_flag; + +/* nghttp2_inflight_settings stores the SETTINGS entries which local + endpoint has sent to the remote endpoint, and has not received ACK + yet. */ +struct nghttp2_inflight_settings { + struct nghttp2_inflight_settings *next; + nghttp2_settings_entry *iv; + size_t niv; +}; + +typedef struct nghttp2_inflight_settings nghttp2_inflight_settings; + +struct nghttp2_session { + nghttp2_map /* */ streams; + /* root of dependency tree*/ + nghttp2_stream root; + /* Queue for outbound urgent frames (PING and SETTINGS) */ + nghttp2_outbound_queue ob_urgent; + /* Queue for non-DATA frames */ + nghttp2_outbound_queue ob_reg; + /* Queue for outbound stream-creating HEADERS (request or push + response) frame, which are subject to + SETTINGS_MAX_CONCURRENT_STREAMS limit. */ + nghttp2_outbound_queue ob_syn; + nghttp2_active_outbound_item aob; + nghttp2_inbound_frame iframe; + nghttp2_hd_deflater hd_deflater; + nghttp2_hd_inflater hd_inflater; + nghttp2_session_callbacks callbacks; + /* Memory allocator */ + nghttp2_mem mem; + void *user_data; + /* Points to the latest incoming closed stream. NULL if there is no + closed stream. Only used when session is initialized as + server. */ + nghttp2_stream *closed_stream_head; + /* Points to the oldest incoming closed stream. NULL if there is no + closed stream. Only used when session is initialized as + server. */ + nghttp2_stream *closed_stream_tail; + /* Points to the latest idle stream. NULL if there is no idle + stream. Only used when session is initialized as server .*/ + nghttp2_stream *idle_stream_head; + /* Points to the oldest idle stream. NULL if there is no idle + stream. Only used when session is initialized as erver. */ + nghttp2_stream *idle_stream_tail; + /* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not + considered as in-flight. */ + nghttp2_inflight_settings *inflight_settings_head; + /* The number of outgoing streams. This will be capped by + remote_settings.max_concurrent_streams. */ + size_t num_outgoing_streams; + /* The number of incoming streams. This will be capped by + local_settings.max_concurrent_streams. */ + size_t num_incoming_streams; + /* The number of incoming reserved streams. This is the number of + streams in reserved (remote) state. RFC 7540 does not limit this + number. nghttp2 offers + nghttp2_option_set_max_reserved_remote_streams() to achieve this. + If it is used, num_incoming_streams is capped by + max_incoming_reserved_streams. Client application should + consider to set this because without that server can send + arbitrary number of PUSH_PROMISE, and exhaust client's memory. */ + size_t num_incoming_reserved_streams; + /* The maximum number of incoming reserved streams (reserved + (remote) state). RST_STREAM will be sent for the pushed stream + which exceeds this limit. */ + size_t max_incoming_reserved_streams; + /* The number of closed streams still kept in |streams| hash. The + closed streams can be accessed through single linked list + |closed_stream_head|. The current implementation only keeps + incoming streams and session is initialized as server. */ + size_t num_closed_streams; + /* The number of idle streams kept in |streams| hash. The idle + streams can be accessed through doubly linked list + |idle_stream_head|. The current implementation only keeps idle + streams if session is initialized as server. */ + size_t num_idle_streams; + /* The number of bytes allocated for nvbuf */ + size_t nvbuflen; + /* Counter for detecting flooding in outbound queue. If it exceeds + max_outbound_ack, session will be closed. */ + size_t obq_flood_counter_; + /* The maximum number of outgoing SETTINGS ACK and PING ACK in + outbound queue. */ + size_t max_outbound_ack; + /* The maximum length of header block to send. Calculated by the + same way as nghttp2_hd_deflate_bound() does. */ + size_t max_send_header_block_length; + /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ + uint32_t next_stream_id; + /* The last stream ID this session initiated. For client session, + this is the last stream ID it has sent. For server session, it + is the last promised stream ID sent in PUSH_PROMISE. */ + int32_t last_sent_stream_id; + /* The largest stream ID received so far */ + int32_t last_recv_stream_id; + /* The largest stream ID which has been processed in some way. This + value will be used as last-stream-id when sending GOAWAY + frame. */ + int32_t last_proc_stream_id; + /* Counter of unique ID of PING. Wraps when it exceeds + NGHTTP2_MAX_UNIQUE_ID */ + uint32_t next_unique_id; + /* This is the last-stream-ID we have sent in GOAWAY */ + int32_t local_last_stream_id; + /* This is the value in GOAWAY frame received from remote endpoint. */ + int32_t remote_last_stream_id; + /* Current sender window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t remote_window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. This could be negative after submitting negative + value to WINDOW_UPDATE. */ + int32_t recv_window_size; + /* The number of bytes consumed by the application and now is + subject to WINDOW_UPDATE. This is only used when auto + WINDOW_UPDATE is turned off. */ + int32_t consumed_size; + /* The amount of recv_window_size cut using submitting negative + value to WINDOW_UPDATE */ + int32_t recv_reduction; + /* window size for local flow control. It is initially set to + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE and could be + increased/decreased by submitting WINDOW_UPDATE. See + nghttp2_submit_window_update(). */ + int32_t local_window_size; + /* This flag is used to indicate that the local endpoint received initial + SETTINGS frame from the remote endpoint. */ + uint8_t remote_settings_received; + /* Settings value received from the remote endpoint. */ + nghttp2_settings_storage remote_settings; + /* Settings value of the local endpoint. */ + nghttp2_settings_storage local_settings; + /* Option flags. This is bitwise-OR of 0 or more of nghttp2_optmask. */ + uint32_t opt_flags; + /* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this + to refuse the incoming stream if it exceeds this value. */ + uint32_t pending_local_max_concurrent_stream; + /* The bitwise OR of zero or more of nghttp2_typemask to indicate + that the default handling of extension frame is enabled. */ + uint32_t builtin_recv_ext_types; + /* Unacked local ENABLE_PUSH value. We use this to refuse + PUSH_PROMISE before SETTINGS ACK is received. */ + uint8_t pending_enable_push; + /* Unacked local ENABLE_CONNECT_PROTOCOL value. We use this to + accept :protocol header field before SETTINGS_ACK is received. */ + uint8_t pending_enable_connect_protocol; + /* Nonzero if the session is server side. */ + uint8_t server; + /* Flags indicating GOAWAY is sent and/or received. The flags are + composed by bitwise OR-ing nghttp2_goaway_flag. */ + uint8_t goaway_flags; + /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to + this session. The nonzero does not necessarily mean + WINDOW_UPDATE is not queued. */ + uint8_t window_update_queued; + /* Bitfield of extension frame types that application is willing to + receive. To designate the bit of given frame type i, use + user_recv_ext_types[i / 8] & (1 << (i & 0x7)). First 10 frame + types are standard frame types and not used in this bitfield. If + bit is set, it indicates that incoming frame with that type is + passed to user defined callbacks, otherwise they are ignored. */ + uint8_t user_recv_ext_types[32]; +}; + +/* Struct used when updating initial window size of each active + stream. */ +typedef struct { + nghttp2_session *session; + int32_t new_window_size, old_window_size; +} nghttp2_update_window_size_arg; + +typedef struct { + nghttp2_session *session; + /* linked list of streams to close */ + nghttp2_stream *head; + int32_t last_stream_id; + /* nonzero if GOAWAY is sent to peer, which means we are going to + close incoming streams. zero if GOAWAY is received from peer and + we are going to close outgoing streams. */ + int incoming; +} nghttp2_close_stream_on_goaway_arg; + +/* TODO stream timeout etc */ + +/* + * Returns nonzero value if |stream_id| is initiated by local + * endpoint. + */ +int nghttp2_session_is_my_stream_id(nghttp2_session *session, + int32_t stream_id); + +/* + * Adds |item| to the outbound queue in |session|. When this function + * succeeds, it takes ownership of |item|. So caller must not free it + * on success. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_STREAM_CLOSED + * Stream already closed (DATA and PUSH_PROMISE frame only) + */ +int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item); + +/* + * Adds RST_STREAM frame for the stream |stream_id| with the error + * code |error_code|. This is a convenient function built on top of + * nghttp2_session_add_frame() to add RST_STREAM easily. + * + * This function simply returns 0 without adding RST_STREAM frame if + * given stream is in NGHTTP2_STREAM_CLOSING state, because multiple + * RST_STREAM for a stream is redundant. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code); + +/* + * Adds PING frame. This is a convenient functin built on top of + * nghttp2_session_add_frame() to add PING easily. + * + * If the |opaque_data| is not NULL, it must point to 8 bytes memory + * region of data. The data pointed by |opaque_data| is copied. It can + * be NULL. In this case, 8 bytes NULL is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FLOODED + * There are too many items in outbound queue; this only happens + * if NGHTTP2_FLAG_ACK is set in |flags| + */ +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/* + * Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the + * error code |error_code|. This is a convenient function built on top + * of nghttp2_session_add_frame() to add GOAWAY easily. The + * |aux_flags| are bitwise-OR of one or more of + * nghttp2_goaway_aux_flag. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * The |opaque_data_len| is too large. + */ +int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, + uint32_t error_code, const uint8_t *opaque_data, + size_t opaque_data_len, uint8_t aux_flags); + +/* + * Adds WINDOW_UPDATE frame with stream ID |stream_id| and + * window-size-increment |window_size_increment|. This is a convenient + * function built on top of nghttp2_session_add_frame() to add + * WINDOW_UPDATE easily. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/* + * Adds SETTINGS frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FLOODED + * There are too many items in outbound queue; this only happens + * if NGHTTP2_FLAG_ACK is set in |flags| + */ +int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv); + +/* + * Creates new stream in |session| with stream ID |stream_id|, + * priority |pri_spec| and flags |flags|. The |flags| is bitwise OR + * of nghttp2_stream_flag. Since this function is called when initial + * HEADERS is sent or received, these flags are taken from it. The + * state of stream is set to |initial_state|. The |stream_user_data| + * is a pointer to the arbitrary user supplied data to be associated + * to this stream. + * + * If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets + * NGHTTP2_STREAM_FLAG_PUSH flag set. + * + * This function returns a pointer to created new stream object, or + * NULL. + * + * This function adjusts neither the number of closed streams or idle + * streams. The caller should manually call + * nghttp2_session_adjust_closed_stream() or + * nghttp2_session_adjust_idle_stream() respectively. + */ +nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, + nghttp2_priority_spec *pri_spec, + nghttp2_stream_state initial_state, + void *stream_user_data); + +/* + * Closes stream whose stream ID is |stream_id|. The reason of closure + * is indicated by the |error_code|. When closing the stream, + * on_stream_close_callback will be called. + * + * If the session is initialized as server and |stream| is incoming + * stream, stream is just marked closed and this function calls + * nghttp2_session_keep_closed_stream() with |stream|. Otherwise, + * |stream| will be deleted from memory. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code); + +/* + * Deletes |stream| from memory. After this function returns, stream + * cannot be accessed. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_destroy_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Tries to keep incoming closed stream |stream|. Due to the + * limitation of maximum number of streams in memory, |stream| is not + * closed and just deleted from memory (see + * nghttp2_session_destroy_stream). + */ +void nghttp2_session_keep_closed_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Appends |stream| to linked list |session->idle_stream_head|. We + * apply fixed limit for list size. To fit into that limit, one or + * more oldest streams are removed from list as necessary. + */ +void nghttp2_session_keep_idle_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Detaches |stream| from idle streams linked list. + */ +void nghttp2_session_detach_idle_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Deletes closed stream to ensure that number of incoming streams + * including active and closed is in the maximum number of allowed + * stream. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_adjust_closed_stream(nghttp2_session *session); + +/* + * Deletes idle stream to ensure that number of idle streams is in + * certain limit. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_adjust_idle_stream(nghttp2_session *session); + +/* + * If further receptions and transmissions over the stream |stream_id| + * are disallowed, close the stream with error code NGHTTP2_NO_ERROR. + * + * This function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + */ +int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, + nghttp2_stream *stream); + +int nghttp2_session_on_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame); + +int nghttp2_session_on_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +/* + * Called when HEADERS is received, assuming |frame| is properly + * initialized. This function does first validate received frame and + * then open stream and call callback functions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_IGN_HEADER_BLOCK + * Frame was rejected and header block must be decoded but + * result must be ignored. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +/* + * Called when PRIORITY is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_priority_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when RST_STREAM is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when SETTINGS is received, assuming |frame| is properly + * initialized. If |noack| is non-zero, SETTINGS with ACK will not be + * submitted. If |frame| has NGHTTP2_FLAG_ACK flag set, no SETTINGS + * with ACK will not be submitted regardless of |noack|. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + * NGHTTP2_ERR_FLOODED + * There are too many items in outbound queue, and this is most + * likely caused by misbehaviour of peer. + */ +int nghttp2_session_on_settings_received(nghttp2_session *session, + nghttp2_frame *frame, int noack); + +/* + * Called when PUSH_PROMISE is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_IGN_HEADER_BLOCK + * Frame was rejected and header block must be decoded but + * result must be ignored. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_push_promise_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when PING is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_FLOODED + * There are too many items in outbound queue, and this is most + * likely caused by misbehaviour of peer. + */ +int nghttp2_session_on_ping_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when GOAWAY is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_goaway_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when WINDOW_UPDATE is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_window_update_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when ALTSVC is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_altsvc_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when ORIGIN is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_origin_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when DATA is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_data_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Returns nghttp2_stream* object whose stream ID is |stream_id|. It + * could be NULL if such stream does not exist. This function returns + * NULL if stream is marked as closed. + */ +nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, + int32_t stream_id); + +/* + * This function behaves like nghttp2_session_get_stream(), but it + * returns stream object even if it is marked as closed or in + * NGHTTP2_STREAM_IDLE state. + */ +nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, + int32_t stream_id); + +/* + * Packs DATA frame |frame| in wire frame format and stores it in + * |bufs|. Payload will be read using |aux_data->data_prd|. The + * length of payload is at most |datamax| bytes. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_DEFERRED + * The DATA frame is postponed. + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + * The read_callback failed (stream error). + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed (session error). + */ +int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, + size_t datamax, nghttp2_frame *frame, + nghttp2_data_aux_data *aux_data, + nghttp2_stream *stream); + +/* + * Pops and returns next item to send. If there is no such item, + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_syn has item and max concurrent + * streams is reached, the even if other queues contain items, then + * this function returns NULL. + */ +nghttp2_outbound_item * +nghttp2_session_pop_next_ob_item(nghttp2_session *session); + +/* + * Returns next item to send. If there is no such item, this function + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_syn has item and max concurrent + * streams is reached, the even if other queues contain items, then + * this function returns NULL. + */ +nghttp2_outbound_item * +nghttp2_session_get_next_ob_item(nghttp2_session *session); + +/* + * Updates local settings with the |iv|. The number of elements in the + * array pointed by the |iv| is given by the |niv|. This function + * assumes that the all settings_id member in |iv| are in range 1 to + * NGHTTP2_SETTINGS_MAX, inclusive. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_update_local_settings(nghttp2_session *session, + nghttp2_settings_entry *iv, + size_t niv); + +/* + * Re-prioritize |stream|. The new priority specification is + * |pri_spec|. Caller must ensure that stream->hd.stream_id != + * pri_spec->stream_id. + * + * This function does not adjust the number of idle streams. The + * caller should call nghttp2_session_adjust_idle_stream() later. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_reprioritize_stream(nghttp2_session *session, + nghttp2_stream *stream, + const nghttp2_priority_spec *pri_spec); + +/* + * Terminates current |session| with the |error_code|. The |reason| + * is NULL-terminated debug string. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * The |reason| is too long. + */ +int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, + uint32_t error_code, + const char *reason); + +#endif /* NGHTTP2_SESSION_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_stream.c b/Utilities/cmnghttp2/lib/nghttp2_stream.c new file mode 100644 index 00000000000..dc3a6b11ccb --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_stream.c @@ -0,0 +1,1001 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_stream.h" + +#include +#include + +#include "nghttp2_session.h" +#include "nghttp2_helper.h" +#include "nghttp2_debug.h" +#include "nghttp2_frame.h" + +/* Maximum distance between any two stream's cycle in the same + prirority queue. Imagine stream A's cycle is A, and stream B's + cycle is B, and A < B. The cycle is unsigned 32 bit integer, it + may get overflow. Because of how we calculate the next cycle + value, if B - A is less than or equals to + NGHTTP2_MAX_CYCLE_DISTANCE, A and B are in the same scale, in other + words, B is really greater than or equal to A. Otherwise, A is a + result of overflow, and it is actually A > B if we consider that + fact. */ +#define NGHTTP2_MAX_CYCLE_DISTANCE \ + ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX * 256 + 255) + +static int stream_less(const void *lhsx, const void *rhsx) { + const nghttp2_stream *lhs, *rhs; + + lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry); + rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry); + + if (lhs->cycle == rhs->cycle) { + return lhs->seq < rhs->seq; + } + + return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE; +} + +void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, + int32_t weight, int32_t remote_initial_window_size, + int32_t local_initial_window_size, + void *stream_user_data, nghttp2_mem *mem) { + nghttp2_map_entry_init(&stream->map_entry, (key_type)stream_id); + nghttp2_pq_init(&stream->obq, stream_less, mem); + + stream->stream_id = stream_id; + stream->flags = flags; + stream->state = initial_state; + stream->shut_flags = NGHTTP2_SHUT_NONE; + stream->stream_user_data = stream_user_data; + stream->item = NULL; + stream->remote_window_size = remote_initial_window_size; + stream->local_window_size = local_initial_window_size; + stream->recv_window_size = 0; + stream->consumed_size = 0; + stream->recv_reduction = 0; + stream->window_update_queued = 0; + + stream->dep_prev = NULL; + stream->dep_next = NULL; + stream->sib_prev = NULL; + stream->sib_next = NULL; + + stream->closed_prev = NULL; + stream->closed_next = NULL; + + stream->weight = weight; + stream->sum_dep_weight = 0; + + stream->http_flags = NGHTTP2_HTTP_FLAG_NONE; + stream->content_length = -1; + stream->recv_content_length = 0; + stream->status_code = -1; + + stream->queued = 0; + stream->descendant_last_cycle = 0; + stream->cycle = 0; + stream->pending_penalty = 0; + stream->descendant_next_seq = 0; + stream->seq = 0; + stream->last_writelen = 0; +} + +void nghttp2_stream_free(nghttp2_stream *stream) { + nghttp2_pq_free(&stream->obq); + /* We don't free stream->item. If it is assigned to aob, then + active_outbound_item_reset() will delete it. Otherwise, + nghttp2_stream_close() or session_del() will delete it. */ +} + +void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) { + stream->shut_flags = (uint8_t)(stream->shut_flags | flag); +} + +/* + * Returns nonzero if |stream| is active. This function does not take + * into account its descendants. + */ +static int stream_active(nghttp2_stream *stream) { + return stream->item && + (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0; +} + +/* + * Returns nonzero if |stream| or one of its descendants is active + */ +static int stream_subtree_active(nghttp2_stream *stream) { + return stream_active(stream) || !nghttp2_pq_empty(&stream->obq); +} + +/* + * Returns next cycle for |stream|. + */ +static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) { + uint64_t penalty; + + penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT + + stream->pending_penalty; + + stream->cycle = last_cycle + penalty / (uint32_t)stream->weight; + stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight); +} + +static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) { + int rv; + + for (; dep_stream && !stream->queued; + stream = dep_stream, dep_stream = dep_stream->dep_prev) { + stream_next_cycle(stream, dep_stream->descendant_last_cycle); + stream->seq = dep_stream->descendant_next_seq++; + + DEBUGF("stream: stream=%d obq push cycle=%lu\n", stream->stream_id, + stream->cycle); + + DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id, + dep_stream->stream_id); + + rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); + if (rv != 0) { + return rv; + } + stream->queued = 1; + } + + return 0; +} + +/* + * Removes |stream| from parent's obq. If removal of |stream| makes + * parent's obq empty, and parent is not active, then parent is also + * removed. This process is repeated recursively. + */ +static void stream_obq_remove(nghttp2_stream *stream) { + nghttp2_stream *dep_stream; + + dep_stream = stream->dep_prev; + + if (!stream->queued) { + return; + } + + for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { + DEBUGF("stream: remove stream %d from stream %d\n", stream->stream_id, + dep_stream->stream_id); + + nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); + + assert(stream->queued); + + stream->queued = 0; + stream->cycle = 0; + stream->pending_penalty = 0; + stream->descendant_last_cycle = 0; + stream->last_writelen = 0; + + if (stream_subtree_active(dep_stream)) { + return; + } + } +} + +/* + * Moves |stream| from |src|'s obq to |dest|'s obq. Removal from + * |src|'s obq is just done calling nghttp2_pq_remove(), so it does + * not recursively remove |src| and ancestors, like + * stream_obq_remove(). + */ +static int stream_obq_move(nghttp2_stream *dest, nghttp2_stream *src, + nghttp2_stream *stream) { + if (!stream->queued) { + return 0; + } + + DEBUGF("stream: remove stream %d from stream %d (move)\n", stream->stream_id, + src->stream_id); + + nghttp2_pq_remove(&src->obq, &stream->pq_entry); + stream->queued = 0; + + return stream_obq_push(dest, stream); +} + +void nghttp2_stream_reschedule(nghttp2_stream *stream) { + nghttp2_stream *dep_stream; + + assert(stream->queued); + + dep_stream = stream->dep_prev; + + for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { + nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); + + stream_next_cycle(stream, dep_stream->descendant_last_cycle); + stream->seq = dep_stream->descendant_next_seq++; + + nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); + + DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id, + stream->cycle); + + dep_stream->last_writelen = stream->last_writelen; + } +} + +void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) { + nghttp2_stream *dep_stream; + uint64_t last_cycle; + int32_t old_weight; + uint64_t wlen_penalty; + + if (stream->weight == weight) { + return; + } + + old_weight = stream->weight; + stream->weight = weight; + + dep_stream = stream->dep_prev; + + if (!dep_stream) { + return; + } + + dep_stream->sum_dep_weight += weight - old_weight; + + if (!stream->queued) { + return; + } + + nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); + + wlen_penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT; + + /* Compute old stream->pending_penalty we used to calculate + stream->cycle */ + stream->pending_penalty = + (uint32_t)((stream->pending_penalty + (uint32_t)old_weight - + (wlen_penalty % (uint32_t)old_weight)) % + (uint32_t)old_weight); + + last_cycle = stream->cycle - + (wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight; + + /* Now we have old stream->pending_penalty and new stream->weight in + place */ + stream_next_cycle(stream, last_cycle); + + if (dep_stream->descendant_last_cycle - stream->cycle <= + NGHTTP2_MAX_CYCLE_DISTANCE) { + stream->cycle = dep_stream->descendant_last_cycle; + } + + /* Continue to use same stream->seq */ + + nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); + + DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id, + stream->cycle); +} + +static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) { + for (; stream->sib_next; stream = stream->sib_next) + ; + + return stream; +} + +int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, + int32_t weight) { + weight = stream->weight * weight / stream->sum_dep_weight; + + return nghttp2_max(1, weight); +} + +#ifdef STREAM_DEP_DEBUG + +static void ensure_inactive(nghttp2_stream *stream) { + nghttp2_stream *si; + + if (stream->queued) { + fprintf(stderr, "stream(%p)=%d, stream->queued = 1; want 0\n", stream, + stream->stream_id); + assert(0); + } + + if (stream_active(stream)) { + fprintf(stderr, "stream(%p)=%d, stream_active(stream) = 1; want 0\n", + stream, stream->stream_id); + assert(0); + } + + if (!nghttp2_pq_empty(&stream->obq)) { + fprintf(stderr, "stream(%p)=%d, nghttp2_pq_size() = %zu; want 0\n", stream, + stream->stream_id, nghttp2_pq_size(&stream->obq)); + assert(0); + } + + for (si = stream->dep_next; si; si = si->sib_next) { + ensure_inactive(si); + } +} + +static void check_queued(nghttp2_stream *stream) { + nghttp2_stream *si; + int queued; + + if (stream->queued) { + if (!stream_subtree_active(stream)) { + fprintf(stderr, + "stream(%p)=%d, stream->queued == 1, but " + "stream_active() == %d and nghttp2_pq_size(&stream->obq) = %zu\n", + stream, stream->stream_id, stream_active(stream), + nghttp2_pq_size(&stream->obq)); + assert(0); + } + if (!stream_active(stream)) { + queued = 0; + for (si = stream->dep_next; si; si = si->sib_next) { + if (si->queued) { + ++queued; + } + } + if (queued == 0) { + fprintf(stderr, + "stream(%p)=%d, stream->queued == 1, and " + "!stream_active(), but no descendants is queued\n", + stream, stream->stream_id); + assert(0); + } + } + + for (si = stream->dep_next; si; si = si->sib_next) { + check_queued(si); + } + } else { + if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) { + fprintf(stderr, + "stream(%p) = %d, stream->queued == 0, but " + "stream_active(stream) == %d and " + "nghttp2_pq_size(&stream->obq) = %zu\n", + stream, stream->stream_id, stream_active(stream), + nghttp2_pq_size(&stream->obq)); + assert(0); + } + for (si = stream->dep_next; si; si = si->sib_next) { + ensure_inactive(si); + } + } +} + +static void check_sum_dep(nghttp2_stream *stream) { + nghttp2_stream *si; + int32_t n = 0; + for (si = stream->dep_next; si; si = si->sib_next) { + n += si->weight; + } + if (n != stream->sum_dep_weight) { + fprintf(stderr, "stream(%p)=%d, sum_dep_weight = %d; want %d\n", stream, + stream->stream_id, n, stream->sum_dep_weight); + assert(0); + } + for (si = stream->dep_next; si; si = si->sib_next) { + check_sum_dep(si); + } +} + +static void check_dep_prev(nghttp2_stream *stream) { + nghttp2_stream *si; + for (si = stream->dep_next; si; si = si->sib_next) { + if (si->dep_prev != stream) { + fprintf(stderr, "si->dep_prev = %p; want %p\n", si->dep_prev, stream); + assert(0); + } + check_dep_prev(si); + } +} + +#endif /* STREAM_DEP_DEBUG */ + +#ifdef STREAM_DEP_DEBUG +static void validate_tree(nghttp2_stream *stream) { + nghttp2_stream *si; + + if (!stream) { + return; + } + + for (; stream->dep_prev; stream = stream->dep_prev) + ; + + assert(stream->stream_id == 0); + assert(!stream->queued); + + fprintf(stderr, "checking...\n"); + if (nghttp2_pq_empty(&stream->obq)) { + fprintf(stderr, "root obq empty\n"); + for (si = stream->dep_next; si; si = si->sib_next) { + ensure_inactive(si); + } + } else { + for (si = stream->dep_next; si; si = si->sib_next) { + check_queued(si); + } + } + + check_sum_dep(stream); + check_dep_prev(stream); +} +#else /* !STREAM_DEP_DEBUG */ +static void validate_tree(nghttp2_stream *stream) { (void)stream; } +#endif /* !STREAM_DEP_DEBUG*/ + +static int stream_update_dep_on_attach_item(nghttp2_stream *stream) { + int rv; + + rv = stream_obq_push(stream->dep_prev, stream); + if (rv != 0) { + return rv; + } + + validate_tree(stream); + return 0; +} + +static int stream_update_dep_on_detach_item(nghttp2_stream *stream) { + if (nghttp2_pq_empty(&stream->obq)) { + stream_obq_remove(stream); + } + + validate_tree(stream); + + return 0; +} + +int nghttp2_stream_attach_item(nghttp2_stream *stream, + nghttp2_outbound_item *item) { + int rv; + + assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0); + assert(stream->item == NULL); + + DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item); + + stream->item = item; + + rv = stream_update_dep_on_attach_item(stream); + if (rv != 0) { + /* This may relave stream->queued == 1, but stream->item == NULL. + But only consequence of this error is fatal one, and session + destruction. In that execution path, these inconsistency does + not matter. */ + stream->item = NULL; + return rv; + } + + return 0; +} + +int nghttp2_stream_detach_item(nghttp2_stream *stream) { + DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item); + + stream->item = NULL; + stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL); + + return stream_update_dep_on_detach_item(stream); +} + +int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { + assert(stream->item); + + DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id, + stream->item, flags); + + stream->flags |= flags; + + return stream_update_dep_on_detach_item(stream); +} + +int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) { + assert(stream->item); + + DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id, + stream->item, flags); + + stream->flags = (uint8_t)(stream->flags & ~flags); + + if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) { + return 0; + } + + return stream_update_dep_on_attach_item(stream); +} + +int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) { + return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL); +} + +int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) { + return stream->item && + (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); +} + +static int update_initial_window_size(int32_t *window_size_ptr, + int32_t new_initial_window_size, + int32_t old_initial_window_size) { + int64_t new_window_size = (int64_t)(*window_size_ptr) + + new_initial_window_size - old_initial_window_size; + if (INT32_MIN > new_window_size || + new_window_size > NGHTTP2_MAX_WINDOW_SIZE) { + return -1; + } + *window_size_ptr = (int32_t)new_window_size; + return 0; +} + +int nghttp2_stream_update_remote_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size) { + return update_initial_window_size(&stream->remote_window_size, + new_initial_window_size, + old_initial_window_size); +} + +int nghttp2_stream_update_local_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size) { + return update_initial_window_size(&stream->local_window_size, + new_initial_window_size, + old_initial_window_size); +} + +void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) { + stream->state = NGHTTP2_STREAM_OPENED; + stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH); +} + +int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream, + nghttp2_stream *target) { + for (; stream; stream = stream->dep_prev) { + if (stream == target) { + return 1; + } + } + return 0; +} + +int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *si; + int rv; + + DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream, + dep_stream->stream_id, stream, stream->stream_id); + + stream->sum_dep_weight = dep_stream->sum_dep_weight; + dep_stream->sum_dep_weight = stream->weight; + + if (dep_stream->dep_next) { + for (si = dep_stream->dep_next; si; si = si->sib_next) { + si->dep_prev = stream; + if (si->queued) { + rv = stream_obq_move(stream, dep_stream, si); + if (rv != 0) { + return rv; + } + } + } + + if (stream_subtree_active(stream)) { + rv = stream_obq_push(dep_stream, stream); + if (rv != 0) { + return rv; + } + } + + stream->dep_next = dep_stream->dep_next; + } + + dep_stream->dep_next = stream; + stream->dep_prev = dep_stream; + + validate_tree(stream); + + return 0; +} + +static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) { + for (; stream; stream = stream->sib_next) { + stream->dep_prev = dep; + } +} + +static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) { + dep_stream->dep_next = stream; + if (stream) { + stream->dep_prev = dep_stream; + } +} + +static void link_sib(nghttp2_stream *a, nghttp2_stream *b) { + a->sib_next = b; + if (b) { + b->sib_prev = a; + } +} + +static void insert_link_dep(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *sib_next; + + assert(stream->sib_prev == NULL); + + sib_next = dep_stream->dep_next; + + link_sib(stream, sib_next); + + link_dep(dep_stream, stream); +} + +static void unlink_sib(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_next; + + prev = stream->sib_prev; + dep_next = stream->dep_next; + + assert(prev); + + if (dep_next) { + /* + * prev--stream(--sib_next--...) + * | + * dep_next + */ + + link_sib(prev, dep_next); + + set_dep_prev(dep_next, stream->dep_prev); + + if (stream->sib_next) { + link_sib(stream_last_sib(dep_next), stream->sib_next); + } + } else { + /* + * prev--stream(--sib_next--...) + */ + next = stream->sib_next; + + prev->sib_next = next; + + if (next) { + next->sib_prev = prev; + } + } +} + +static void unlink_dep(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_next; + + prev = stream->dep_prev; + dep_next = stream->dep_next; + + assert(prev); + + if (dep_next) { + /* + * prev + * | + * stream(--sib_next--...) + * | + * dep_next + */ + link_dep(prev, dep_next); + + set_dep_prev(dep_next, stream->dep_prev); + + if (stream->sib_next) { + link_sib(stream_last_sib(dep_next), stream->sib_next); + } + + } else if (stream->sib_next) { + /* + * prev + * | + * stream--sib_next + */ + next = stream->sib_next; + + next->sib_prev = NULL; + + link_dep(prev, next); + } else { + prev->dep_next = NULL; + } +} + +void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream, + dep_stream->stream_id, stream, stream->stream_id); + + dep_stream->sum_dep_weight += stream->weight; + + if (dep_stream->dep_next == NULL) { + link_dep(dep_stream, stream); + } else { + insert_link_dep(dep_stream, stream); + } + + validate_tree(stream); +} + +int nghttp2_stream_dep_remove(nghttp2_stream *stream) { + nghttp2_stream *dep_prev, *si; + int32_t sum_dep_weight_delta; + int rv; + + DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id); + + /* Distribute weight of |stream| to direct descendants */ + sum_dep_weight_delta = -stream->weight; + + for (si = stream->dep_next; si; si = si->sib_next) { + si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight); + + sum_dep_weight_delta += si->weight; + + if (si->queued) { + rv = stream_obq_move(stream->dep_prev, stream, si); + if (rv != 0) { + return rv; + } + } + } + + assert(stream->dep_prev); + + dep_prev = stream->dep_prev; + + dep_prev->sum_dep_weight += sum_dep_weight_delta; + + if (stream->queued) { + stream_obq_remove(stream); + } + + if (stream->sib_prev) { + unlink_sib(stream); + } else { + unlink_dep(stream); + } + + stream->sum_dep_weight = 0; + + stream->dep_prev = NULL; + stream->dep_next = NULL; + stream->sib_prev = NULL; + stream->sib_next = NULL; + + validate_tree(dep_prev); + + return 0; +} + +int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *last_sib; + nghttp2_stream *dep_next; + nghttp2_stream *si; + int rv; + + DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id); + + stream->sum_dep_weight += dep_stream->sum_dep_weight; + dep_stream->sum_dep_weight = stream->weight; + + if (dep_stream->dep_next) { + dep_next = dep_stream->dep_next; + + link_dep(dep_stream, stream); + + if (stream->dep_next) { + last_sib = stream_last_sib(stream->dep_next); + + link_sib(last_sib, dep_next); + } else { + link_dep(stream, dep_next); + } + + for (si = dep_next; si; si = si->sib_next) { + si->dep_prev = stream; + if (si->queued) { + rv = stream_obq_move(stream, dep_stream, si); + if (rv != 0) { + return rv; + } + } + } + } else { + link_dep(dep_stream, stream); + } + + if (stream_subtree_active(stream)) { + rv = stream_obq_push(dep_stream, stream); + if (rv != 0) { + return rv; + } + } + + validate_tree(dep_stream); + + return 0; +} + +int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + int rv; + + DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id); + + dep_stream->sum_dep_weight += stream->weight; + + if (dep_stream->dep_next) { + insert_link_dep(dep_stream, stream); + } else { + link_dep(dep_stream, stream); + } + + if (stream_subtree_active(stream)) { + rv = stream_obq_push(dep_stream, stream); + if (rv != 0) { + return rv; + } + } + + validate_tree(dep_stream); + + return 0; +} + +void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) { + nghttp2_stream *next, *dep_prev; + + DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream, + stream->stream_id); + + assert(stream->dep_prev); + + dep_prev = stream->dep_prev; + + if (stream->sib_prev) { + link_sib(stream->sib_prev, stream->sib_next); + } else { + next = stream->sib_next; + + link_dep(dep_prev, next); + + if (next) { + next->sib_prev = NULL; + } + } + + dep_prev->sum_dep_weight -= stream->weight; + + if (stream->queued) { + stream_obq_remove(stream); + } + + validate_tree(dep_prev); + + stream->sib_prev = NULL; + stream->sib_next = NULL; + stream->dep_prev = NULL; +} + +int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) { + return stream->dep_prev || stream->dep_next || stream->sib_prev || + stream->sib_next; +} + +nghttp2_outbound_item * +nghttp2_stream_next_outbound_item(nghttp2_stream *stream) { + nghttp2_pq_entry *ent; + nghttp2_stream *si; + + for (;;) { + if (stream_active(stream)) { + /* Update ascendant's descendant_last_cycle here, so that we can + assure that new stream is scheduled based on it. */ + for (si = stream; si->dep_prev; si = si->dep_prev) { + si->dep_prev->descendant_last_cycle = si->cycle; + } + return stream->item; + } + ent = nghttp2_pq_top(&stream->obq); + if (!ent) { + return NULL; + } + stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry); + } +} + +nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) { + if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) { + return NGHTTP2_STREAM_STATE_CLOSED; + } + + if (stream->flags & NGHTTP2_STREAM_FLAG_PUSH) { + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + return NGHTTP2_STREAM_STATE_RESERVED_LOCAL; + } + + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return NGHTTP2_STREAM_STATE_RESERVED_REMOTE; + } + } + + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + return NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE; + } + + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL; + } + + if (stream->state == NGHTTP2_STREAM_IDLE) { + return NGHTTP2_STREAM_STATE_IDLE; + } + + return NGHTTP2_STREAM_STATE_OPEN; +} + +nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) { + return stream->dep_prev; +} + +nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) { + return stream->sib_next; +} + +nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) { + return stream->sib_prev; +} + +nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) { + return stream->dep_next; +} + +int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) { + return stream->weight; +} + +int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) { + return stream->sum_dep_weight; +} + +int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) { + return stream->stream_id; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_stream.h b/Utilities/cmnghttp2/lib/nghttp2_stream.h new file mode 100644 index 00000000000..a1b807d295c --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_stream.h @@ -0,0 +1,437 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_STREAM_H +#define NGHTTP2_STREAM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_outbound_item.h" +#include "nghttp2_map.h" +#include "nghttp2_pq.h" +#include "nghttp2_int.h" + +/* + * If local peer is stream initiator: + * NGHTTP2_STREAM_OPENING : upon sending request HEADERS + * NGHTTP2_STREAM_OPENED : upon receiving response HEADERS + * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM + * + * If remote peer is stream initiator: + * NGHTTP2_STREAM_OPENING : upon receiving request HEADERS + * NGHTTP2_STREAM_OPENED : upon sending response HEADERS + * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM + */ +typedef enum { + /* Initial state */ + NGHTTP2_STREAM_INITIAL, + /* For stream initiator: request HEADERS has been sent, but response + HEADERS has not been received yet. For receiver: request HEADERS + has been received, but it does not send response HEADERS yet. */ + NGHTTP2_STREAM_OPENING, + /* For stream initiator: response HEADERS is received. For receiver: + response HEADERS is sent. */ + NGHTTP2_STREAM_OPENED, + /* RST_STREAM is received, but somehow we need to keep stream in + memory. */ + NGHTTP2_STREAM_CLOSING, + /* PUSH_PROMISE is received or sent */ + NGHTTP2_STREAM_RESERVED, + /* Stream is created in this state if it is used as anchor in + dependency tree. */ + NGHTTP2_STREAM_IDLE +} nghttp2_stream_state; + +typedef enum { + NGHTTP2_SHUT_NONE = 0, + /* Indicates further receptions will be disallowed. */ + NGHTTP2_SHUT_RD = 0x01, + /* Indicates further transmissions will be disallowed. */ + NGHTTP2_SHUT_WR = 0x02, + /* Indicates both further receptions and transmissions will be + disallowed. */ + NGHTTP2_SHUT_RDWR = NGHTTP2_SHUT_RD | NGHTTP2_SHUT_WR +} nghttp2_shut_flag; + +typedef enum { + NGHTTP2_STREAM_FLAG_NONE = 0, + /* Indicates that this stream is pushed stream and not opened + yet. */ + NGHTTP2_STREAM_FLAG_PUSH = 0x01, + /* Indicates that this stream was closed */ + NGHTTP2_STREAM_FLAG_CLOSED = 0x02, + /* Indicates the item is deferred due to flow control. */ + NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04, + /* Indicates the item is deferred by user callback */ + NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08, + /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and + NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ + NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c + +} nghttp2_stream_flag; + +/* HTTP related flags to enforce HTTP semantics */ +typedef enum { + NGHTTP2_HTTP_FLAG_NONE = 0, + /* header field seen so far */ + NGHTTP2_HTTP_FLAG__AUTHORITY = 1, + NGHTTP2_HTTP_FLAG__PATH = 1 << 1, + NGHTTP2_HTTP_FLAG__METHOD = 1 << 2, + NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3, + /* host is not pseudo header, but we require either host or + :authority */ + NGHTTP2_HTTP_FLAG_HOST = 1 << 4, + NGHTTP2_HTTP_FLAG__STATUS = 1 << 5, + /* required header fields for HTTP request except for CONNECT + method. */ + NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD | + NGHTTP2_HTTP_FLAG__PATH | + NGHTTP2_HTTP_FLAG__SCHEME, + NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, + /* HTTP method flags */ + NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7, + NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8, + NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9, + NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND = 1 << 10, + NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT | + NGHTTP2_HTTP_FLAG_METH_HEAD | + NGHTTP2_HTTP_FLAG_METH_OPTIONS | + NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND, + /* :path category */ + /* path starts with "/" */ + NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 11, + /* path "*" */ + NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 12, + /* scheme */ + /* "http" or "https" scheme */ + NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13, + /* set if final response is expected */ + NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14, + NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15, +} nghttp2_http_flag; + +struct nghttp2_stream { + /* Intrusive Map */ + nghttp2_map_entry map_entry; + /* Entry for dep_prev->obq */ + nghttp2_pq_entry pq_entry; + /* Priority Queue storing direct descendant (nghttp2_stream). Only + streams which itself has some data to send, or has a descendant + which has some data to sent. */ + nghttp2_pq obq; + /* Content-Length of request/response body. -1 if unknown. */ + int64_t content_length; + /* Received body so far */ + int64_t recv_content_length; + /* Base last_cycle for direct descendent streams. */ + uint64_t descendant_last_cycle; + /* Next scheduled time to sent item */ + uint64_t cycle; + /* Next seq used for direct descendant streams */ + uint64_t descendant_next_seq; + /* Secondary key for prioritization to break a tie for cycle. This + value is monotonically increased for single parent stream. */ + uint64_t seq; + /* pointers to form dependency tree. If multiple streams depend on + a stream, only one stream (left most) has non-NULL dep_prev which + points to the stream it depends on. The remaining streams are + linked using sib_prev and sib_next. The stream which has + non-NULL dep_prev always NULL sib_prev. The right most stream + has NULL sib_next. If this stream is a root of dependency tree, + dep_prev and sib_prev are NULL. */ + nghttp2_stream *dep_prev, *dep_next; + nghttp2_stream *sib_prev, *sib_next; + /* When stream is kept after closure, it may be kept in doubly + linked list pointed by nghttp2_session closed_stream_head. + closed_next points to the next stream object if it is the element + of the list. */ + nghttp2_stream *closed_prev, *closed_next; + /* The arbitrary data provided by user for this stream. */ + void *stream_user_data; + /* Item to send */ + nghttp2_outbound_item *item; + /* Last written length of frame payload */ + size_t last_writelen; + /* stream ID */ + int32_t stream_id; + /* Current remote window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t remote_window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. This could be negative after submitting negative + value to WINDOW_UPDATE */ + int32_t recv_window_size; + /* The number of bytes consumed by the application and now is + subject to WINDOW_UPDATE. This is only used when auto + WINDOW_UPDATE is turned off. */ + int32_t consumed_size; + /* The amount of recv_window_size cut using submitting negative + value to WINDOW_UPDATE */ + int32_t recv_reduction; + /* window size for local flow control. It is initially set to + NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by + submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ + int32_t local_window_size; + /* weight of this stream */ + int32_t weight; + /* This is unpaid penalty (offset) when calculating cycle. */ + uint32_t pending_penalty; + /* sum of weight of direct descendants */ + int32_t sum_dep_weight; + nghttp2_stream_state state; + /* status code from remote server */ + int16_t status_code; + /* Bitwise OR of zero or more nghttp2_http_flag values */ + uint16_t http_flags; + /* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */ + uint8_t flags; + /* Bitwise OR of zero or more nghttp2_shut_flag values */ + uint8_t shut_flags; + /* Nonzero if this stream has been queued to stream pointed by + dep_prev. We maintain the invariant that if a stream is queued, + then its ancestors, except for root, are also queued. This + invariant may break in fatal error condition. */ + uint8_t queued; + /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to + this stream. The nonzero does not necessarily mean WINDOW_UPDATE + is not queued. */ + uint8_t window_update_queued; +}; + +void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, + int32_t weight, int32_t remote_initial_window_size, + int32_t local_initial_window_size, + void *stream_user_data, nghttp2_mem *mem); + +void nghttp2_stream_free(nghttp2_stream *stream); + +/* + * Disallow either further receptions or transmissions, or both. + * |flag| is bitwise OR of one or more of nghttp2_shut_flag. + */ +void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag); + +/* + * Defer |stream->item|. We won't call this function in the situation + * where |stream->item| == NULL. The |flags| is bitwise OR of zero or + * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates + * the reason of this action. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags); + +/* + * Put back deferred data in this stream to active state. The |flags| + * are one or more of bitwise OR of the following values: + * NGHTTP2_STREAM_FLAG_DEFERRED_USER and + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are + * cleared if they are set. So even if this function is called, if + * one of flag is still set, data does not become active. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags); + +/* + * Returns nonzero if item is deferred by whatever reason. + */ +int nghttp2_stream_check_deferred_item(nghttp2_stream *stream); + +/* + * Returns nonzero if item is deferred by flow control. + */ +int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream); + +/* + * Updates the remote window size with the new value + * |new_initial_window_size|. The |old_initial_window_size| is used to + * calculate the current window size. + * + * This function returns 0 if it succeeds or -1. The failure is due to + * overflow. + */ +int nghttp2_stream_update_remote_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size); + +/* + * Updates the local window size with the new value + * |new_initial_window_size|. The |old_initial_window_size| is used to + * calculate the current window size. + * + * This function returns 0 if it succeeds or -1. The failure is due to + * overflow. + */ +int nghttp2_stream_update_local_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size); + +/* + * Call this function if promised stream |stream| is replied with + * HEADERS. This function makes the state of the |stream| to + * NGHTTP2_STREAM_OPENED. + */ +void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream); + +/* + * Returns nonzero if |target| is an ancestor of |stream|. + */ +int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream, + nghttp2_stream *target); + +/* + * Computes distributed weight of a stream of the |weight| under the + * |stream| if |stream| is removed from a dependency tree. + */ +int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, + int32_t weight); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * exclusive. All existing direct descendants of |dep_stream| become + * the descendants of the |stream|. This function assumes + * |stream->item| is NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, + nghttp2_stream *stream); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * not exclusive. This function assumes |stream->item| is NULL. + */ +void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream); + +/* + * Removes the |stream| from the current dependency tree. This + * function assumes |stream->item| is NULL. + */ +int nghttp2_stream_dep_remove(nghttp2_stream *stream); + +/* + * Attaches |item| to |stream|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_attach_item(nghttp2_stream *stream, + nghttp2_outbound_item *item); + +/* + * Detaches |stream->item|. This function does not free + * |stream->item|. The caller must free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_detach_item(nghttp2_stream *stream); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * exclusive. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * not exclusive. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream); + +/* + * Removes subtree whose root stream is |stream|. The + * effective_weight of streams in removed subtree is not updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream); + +/* + * Returns nonzero if |stream| is in any dependency tree. + */ +int nghttp2_stream_in_dep_tree(nghttp2_stream *stream); + +/* + * Schedules transmission of |stream|'s item, assuming stream->item is + * attached, and stream->last_writelen was updated. + */ +void nghttp2_stream_reschedule(nghttp2_stream *stream); + +/* + * Changes |stream|'s weight to |weight|. If |stream| is queued, it + * will be rescheduled based on new weight. + */ +void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight); + +/* + * Returns a stream which has highest priority, updating + * descendant_last_cycle of selected stream's ancestors. + */ +nghttp2_outbound_item * +nghttp2_stream_next_outbound_item(nghttp2_stream *stream); + +#endif /* NGHTTP2_STREAM */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_submit.c b/Utilities/cmnghttp2/lib/nghttp2_submit.c new file mode 100644 index 00000000000..f604eff5c90 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_submit.c @@ -0,0 +1,814 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_submit.h" + +#include +#include + +#include "nghttp2_session.h" +#include "nghttp2_frame.h" +#include "nghttp2_helper.h" +#include "nghttp2_priority_spec.h" + +/* + * Detects the dependency error, that is stream attempted to depend on + * itself. If |stream_id| is -1, we use session->next_stream_id as + * stream ID. + * + * This function returns 0 if it succeeds, or one of the following + * error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * Stream attempted to depend on itself. + */ +static int detect_self_dependency(nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + assert(pri_spec); + + if (stream_id == -1) { + if ((int32_t)session->next_stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + return 0; + } + + if (stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + return 0; +} + +/* This function takes ownership of |nva_copy|. Regardless of the + return value, the caller must not free |nva_copy| after this + function returns. */ +static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva_copy, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) { + int rv; + uint8_t flags_copy; + nghttp2_outbound_item *item = NULL; + nghttp2_frame *frame = NULL; + nghttp2_headers_category hcat; + nghttp2_mem *mem; + + mem = &session->mem; + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail; + } + + nghttp2_outbound_item_init(item); + + if (data_prd != NULL && data_prd->read_callback != NULL) { + item->aux_data.headers.data_prd = *data_prd; + } + + item->aux_data.headers.stream_user_data = stream_user_data; + + flags_copy = + (uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) | + NGHTTP2_FLAG_END_HEADERS); + + if (stream_id == -1) { + if (session->next_stream_id > INT32_MAX) { + rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; + goto fail; + } + + stream_id = (int32_t)session->next_stream_id; + session->next_stream_id += 2; + + hcat = NGHTTP2_HCAT_REQUEST; + } else { + /* More specific categorization will be done later. */ + hcat = NGHTTP2_HCAT_HEADERS; + } + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, + pri_spec, nva_copy, nvlen); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_headers_free(&frame->headers, mem); + goto fail2; + } + + if (hcat == NGHTTP2_HCAT_REQUEST) { + return stream_id; + } + + return 0; + +fail: + /* nghttp2_frame_headers_init() takes ownership of nva_copy. */ + nghttp2_nv_array_del(nva_copy, mem); +fail2: + nghttp2_mem_free(mem, item); + + return rv; +} + +static int32_t submit_headers_shared_nva(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) { + int rv; + nghttp2_nv *nva_copy; + nghttp2_priority_spec copy_pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if (pri_spec) { + copy_pri_spec = *pri_spec; + nghttp2_priority_spec_normalize_weight(©_pri_spec); + } else { + nghttp2_priority_spec_default_init(©_pri_spec); + } + + rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); + if (rv < 0) { + return rv; + } + + return submit_headers_shared(session, flags, stream_id, ©_pri_spec, + nva_copy, nvlen, data_prd, stream_user_data); +} + +int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen) { + if (stream_id <= 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, + stream_id, NULL, nva, nvlen, NULL, + NULL); +} + +int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data) { + int rv; + + if (stream_id == -1) { + if (session->server) { + return NGHTTP2_ERR_PROTO; + } + } else if (stream_id <= 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + flags &= NGHTTP2_FLAG_END_STREAM; + + if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { + rv = detect_self_dependency(session, stream_id, pri_spec); + if (rv != 0) { + return rv; + } + + flags |= NGHTTP2_FLAG_PRIORITY; + } else { + pri_spec = NULL; + } + + return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva, + nvlen, NULL, stream_user_data); +} + +int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data) { + flags &= NGHTTP2_FLAG_ACK; + return nghttp2_session_add_ping(session, flags, opaque_data); +} + +int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_priority_spec copy_pri_spec; + nghttp2_mem *mem; + (void)flags; + + mem = &session->mem; + + if (stream_id == 0 || pri_spec == NULL) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + copy_pri_spec = *pri_spec; + + nghttp2_priority_spec_normalize_weight(©_pri_spec); + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_priority_free(&frame->priority); + nghttp2_mem_free(mem, item); + + return rv; + } + + return 0; +} + +int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, + int32_t stream_id, uint32_t error_code) { + (void)flags; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + return nghttp2_session_add_rst_stream(session, stream_id, error_code); +} + +int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, + int32_t last_stream_id, uint32_t error_code, + const uint8_t *opaque_data, size_t opaque_data_len) { + (void)flags; + + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return 0; + } + return nghttp2_session_add_goaway(session, last_stream_id, error_code, + opaque_data, opaque_data_len, + NGHTTP2_GOAWAY_AUX_NONE); +} + +int nghttp2_submit_shutdown_notice(nghttp2_session *session) { + if (!session->server) { + return NGHTTP2_ERR_INVALID_STATE; + } + if (session->goaway_flags) { + return 0; + } + return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR, + NULL, 0, + NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE); +} + +int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv) { + (void)flags; + return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv); +} + +int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const nghttp2_nv *nva, + size_t nvlen, + void *promised_stream_user_data) { + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_nv *nva_copy; + uint8_t flags_copy; + int32_t promised_stream_id; + int rv; + nghttp2_mem *mem; + (void)flags; + + mem = &session->mem; + + if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + /* All 32bit signed stream IDs are spent. */ + if (session->next_stream_id > INT32_MAX) { + return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + item->aux_data.headers.stream_user_data = promised_stream_user_data; + + frame = &item->frame; + + rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); + if (rv < 0) { + nghttp2_mem_free(mem, item); + return rv; + } + + flags_copy = NGHTTP2_FLAG_END_HEADERS; + + promised_stream_id = (int32_t)session->next_stream_id; + session->next_stream_id += 2; + + nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id, + promised_stream_id, nva_copy, nvlen); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_push_promise_free(&frame->push_promise, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + return promised_stream_id; +} + +int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) { + int rv; + nghttp2_stream *stream = 0; + (void)flags; + + if (window_size_increment == 0) { + return 0; + } + if (stream_id == 0) { + rv = nghttp2_adjust_local_window_size( + &session->local_window_size, &session->recv_window_size, + &session->recv_reduction, &window_size_increment); + if (rv != 0) { + return rv; + } + } else { + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + return 0; + } + + rv = nghttp2_adjust_local_window_size( + &stream->local_window_size, &stream->recv_window_size, + &stream->recv_reduction, &window_size_increment); + if (rv != 0) { + return rv; + } + } + + if (window_size_increment > 0) { + if (stream_id == 0) { + session->consumed_size = + nghttp2_max(0, session->consumed_size - window_size_increment); + } else { + stream->consumed_size = + nghttp2_max(0, stream->consumed_size - window_size_increment); + } + + return nghttp2_session_add_window_update(session, 0, stream_id, + window_size_increment); + } + return 0; +} + +int nghttp2_session_set_local_window_size(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + int32_t window_size) { + int32_t window_size_increment; + nghttp2_stream *stream; + int rv; + (void)flags; + + if (window_size < 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (stream_id == 0) { + window_size_increment = window_size - session->local_window_size; + + if (window_size_increment == 0) { + return 0; + } + + if (window_size_increment < 0) { + return nghttp2_adjust_local_window_size( + &session->local_window_size, &session->recv_window_size, + &session->recv_reduction, &window_size_increment); + } + + rv = nghttp2_increase_local_window_size( + &session->local_window_size, &session->recv_window_size, + &session->recv_reduction, &window_size_increment); + + if (rv != 0) { + return rv; + } + } else { + stream = nghttp2_session_get_stream(session, stream_id); + + if (stream == NULL) { + return 0; + } + + window_size_increment = window_size - stream->local_window_size; + + if (window_size_increment == 0) { + return 0; + } + + if (window_size_increment < 0) { + return nghttp2_adjust_local_window_size( + &stream->local_window_size, &stream->recv_window_size, + &stream->recv_reduction, &window_size_increment); + } + + rv = nghttp2_increase_local_window_size( + &stream->local_window_size, &stream->recv_window_size, + &stream->recv_reduction, &window_size_increment); + + if (rv != 0) { + return rv; + } + } + + if (window_size_increment > 0) { + return nghttp2_session_add_window_update(session, 0, stream_id, + window_size_increment); + } + + return 0; +} + +int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *origin, + size_t origin_len, const uint8_t *field_value, + size_t field_value_len) { + nghttp2_mem *mem; + uint8_t *buf, *p; + uint8_t *origin_copy; + uint8_t *field_value_copy; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_ext_altsvc *altsvc; + int rv; + (void)flags; + + mem = &session->mem; + + if (!session->server) { + return NGHTTP2_ERR_INVALID_STATE; + } + + if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (stream_id == 0) { + if (origin_len == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (origin_len != 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2); + if (buf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + p = buf; + + origin_copy = p; + if (origin_len) { + p = nghttp2_cpymem(p, origin, origin_len); + } + *p++ = '\0'; + + field_value_copy = p; + if (field_value_len) { + p = nghttp2_cpymem(p, field_value, field_value_len); + } + *p++ = '\0'; + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail_item_malloc; + } + + nghttp2_outbound_item_init(item); + + item->aux_data.ext.builtin = 1; + + altsvc = &item->ext_frame_payload.altsvc; + + frame = &item->frame; + frame->ext.payload = altsvc; + + nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len, + field_value_copy, field_value_len); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_altsvc_free(&frame->ext, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + return 0; + +fail_item_malloc: + free(buf); + + return rv; +} + +int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags, + const nghttp2_origin_entry *ov, size_t nov) { + nghttp2_mem *mem; + uint8_t *p; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_ext_origin *origin; + nghttp2_origin_entry *ov_copy; + size_t len = 0; + size_t i; + int rv; + (void)flags; + + mem = &session->mem; + + if (!session->server) { + return NGHTTP2_ERR_INVALID_STATE; + } + + if (nov) { + for (i = 0; i < nov; ++i) { + len += ov[i].origin_len; + } + + if (2 * nov + len > NGHTTP2_MAX_PAYLOADLEN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + /* The last nov is added for terminal NULL character. */ + ov_copy = + nghttp2_mem_malloc(mem, nov * sizeof(nghttp2_origin_entry) + len + nov); + if (ov_copy == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + p = (uint8_t *)ov_copy + nov * sizeof(nghttp2_origin_entry); + + for (i = 0; i < nov; ++i) { + ov_copy[i].origin = p; + ov_copy[i].origin_len = ov[i].origin_len; + p = nghttp2_cpymem(p, ov[i].origin, ov[i].origin_len); + *p++ = '\0'; + } + + assert((size_t)(p - (uint8_t *)ov_copy) == + nov * sizeof(nghttp2_origin_entry) + len + nov); + } else { + ov_copy = NULL; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail_item_malloc; + } + + nghttp2_outbound_item_init(item); + + item->aux_data.ext.builtin = 1; + + origin = &item->ext_frame_payload.origin; + + frame = &item->frame; + frame->ext.payload = origin; + + nghttp2_frame_origin_init(&frame->ext, ov_copy, nov); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_origin_free(&frame->ext, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + return 0; + +fail_item_malloc: + free(ov_copy); + + return rv; +} + +static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec, + const nghttp2_data_provider *data_prd) { + uint8_t flags = NGHTTP2_FLAG_NONE; + if (data_prd == NULL || data_prd->read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; + } + + if (pri_spec) { + flags |= NGHTTP2_FLAG_PRIORITY; + } + + return flags; +} + +int32_t nghttp2_submit_request(nghttp2_session *session, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) { + uint8_t flags; + int rv; + + if (session->server) { + return NGHTTP2_ERR_PROTO; + } + + if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { + rv = detect_self_dependency(session, -1, pri_spec); + if (rv != 0) { + return rv; + } + } else { + pri_spec = NULL; + } + + flags = set_request_flags(pri_spec, data_prd); + + return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen, + data_prd, stream_user_data); +} + +static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) { + uint8_t flags = NGHTTP2_FLAG_NONE; + if (data_prd == NULL || data_prd->read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; + } + return flags; +} + +int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd) { + uint8_t flags; + + if (stream_id <= 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + flags = set_response_flags(data_prd); + return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen, + data_prd, NULL); +} + +int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_data_aux_data *aux_data; + uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + aux_data = &item->aux_data.data; + aux_data->data_prd = *data_prd; + aux_data->eof = 0; + aux_data->flags = nflags; + + /* flags are sent on transmission */ + nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_data_free(&frame->data); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, + const nghttp2_settings_entry *iv, + size_t niv) { + if (!nghttp2_iv_check(iv, niv)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv); +} + +int nghttp2_submit_extension(nghttp2_session *session, uint8_t type, + uint8_t flags, int32_t stream_id, void *payload) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + + if (type <= NGHTTP2_CONTINUATION) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!session->callbacks.pack_extension_callback) { + return NGHTTP2_ERR_INVALID_STATE; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_extension_free(&frame->ext); + nghttp2_mem_free(mem, item); + return rv; + } + + return 0; +} diff --git a/Utilities/cmnghttp2/lib/nghttp2_submit.h b/Utilities/cmnghttp2/lib/nghttp2_submit.h new file mode 100644 index 00000000000..74d702fbcf0 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_submit.h @@ -0,0 +1,34 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_SUBMIT_H +#define NGHTTP2_SUBMIT_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP2_SUBMIT_H */ diff --git a/Utilities/cmnghttp2/lib/nghttp2_version.c b/Utilities/cmnghttp2/lib/nghttp2_version.c new file mode 100644 index 00000000000..4211f2cf8f6 --- /dev/null +++ b/Utilities/cmnghttp2/lib/nghttp2_version.c @@ -0,0 +1,38 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +static nghttp2_info version = {NGHTTP2_VERSION_AGE, NGHTTP2_VERSION_NUM, + NGHTTP2_VERSION, NGHTTP2_PROTO_VERSION_ID}; + +nghttp2_info *nghttp2_version(int least_version) { + if (least_version > NGHTTP2_VERSION_NUM) + return NULL; + return &version; +} diff --git a/Utilities/cmzlib/CMakeLists.txt b/Utilities/cmzlib/CMakeLists.txt index 888c3ff77c9..0584c55605f 100644 --- a/Utilities/cmzlib/CMakeLists.txt +++ b/Utilities/cmzlib/CMakeLists.txt @@ -38,11 +38,6 @@ CONFIGURE_FILE(${CMZLIB_SOURCE_DIR}/.NoDartCoverage CONFIGURE_FILE(${CMZLIB_SOURCE_DIR}/zlibDllConfig.h.in ${CMZLIB_BINARY_DIR}/zlibDllConfig.h) -FOREACH(name zlib zconf cm_zlib_mangle) - CONFIGURE_FILE(${CMZLIB_SOURCE_DIR}/${name}.h - ${CMZLIB_BINARY_DIR}/${name}.h COPYONLY) -ENDFOREACH(name) - ADD_LIBRARY(cmzlib ${ZLIB_SRCS}) diff --git a/Utilities/std/CMakeLists.txt b/Utilities/std/CMakeLists.txt index 63c0a60eae7..17a7aaa4ecc 100644 --- a/Utilities/std/CMakeLists.txt +++ b/Utilities/std/CMakeLists.txt @@ -1,10 +1,15 @@ +# To ensure maximum portability across various compilers and platforms +# deactivate any compiler extensions +set(CMAKE_CXX_EXTENSIONS FALSE) + # source files for CMake std library set(SRCS cm/bits/string_view.cxx cm/memory cm/optional cm/shared_mutex cm/string_view - cm/utility) + cm/utility + cmext/string_view) add_library(cmstd STATIC ${SRCS}) diff --git a/Utilities/std/cm/bits/string_view.cxx b/Utilities/std/cm/bits/string_view.cxx index e345fd3ac10..5381fe618ec 100644 --- a/Utilities/std/cm/bits/string_view.cxx +++ b/Utilities/std/cm/bits/string_view.cxx @@ -9,7 +9,7 @@ # include # include -# include "cm_kwiml.h" +# include namespace cm { diff --git a/Utilities/std/cm/shared_mutex b/Utilities/std/cm/shared_mutex index 2ac9447dc75..ec63a7bc044 100644 --- a/Utilities/std/cm/shared_mutex +++ b/Utilities/std/cm/shared_mutex @@ -17,7 +17,7 @@ # include // IWYU pragma: export #endif #if !defined(CMake_HAVE_CXX_SHARED_MUTEX) -# include "cm_uv.h" +# include #endif namespace cm { diff --git a/Utilities/std/cmext/algorithm b/Utilities/std/cmext/algorithm index 44e61f45dda..251c89a5fa1 100644 --- a/Utilities/std/cmext/algorithm +++ b/Utilities/std/cmext/algorithm @@ -13,12 +13,11 @@ #include #include +#include #if defined(__SUNPRO_CC) && defined(__sparc) # include # include -#else -# include #endif namespace cm { @@ -158,6 +157,95 @@ void append(T& v, U const& r) # endif #endif +#if defined(__SUNPRO_CC) +template +auto contains(Iterator first, Iterator last, Key const& key, + detail::overload_selector<1>) -> decltype(first->first == key) +#else +template ::value && + std::is_convertible::value_type::first_type>::value, + int> = 0> +bool contains(Iterator first, Iterator last, Key const& key) +#endif +{ + return std::find_if( + first, last, + [&key]( + typename std::iterator_traits::value_type const& item) { + return item.first == key; + }) != last; +} + +#if defined(__SUNPRO_CC) +template +bool contains(Iterator first, Iterator last, Key const& key, + detail::overload_selector<0>) +#else +template < + typename Iterator, typename Key, + cm::enable_if_t< + cm::is_input_iterator::value && + std::is_convertible< + Key, typename std::iterator_traits::value_type>::value, + int> = 0> +bool contains(Iterator first, Iterator last, Key const& key) +#endif +{ + return std::find(first, last, key) != last; +} + +#if defined(__SUNPRO_CC) +template +bool contains(Iterator first, Iterator last, Key const& key) +{ + return contains(first, last, key, detail::overload_selector<1>{}); +} +#endif + +#if defined(__SUNPRO_CC) +template +auto contains(Range const& range, Key const& key, detail::overload_selector<1>) + -> decltype(range.find(key) != range.end()) +#else +template < + typename Range, typename Key, + cm::enable_if_t::value || + cm::is_unordered_associative_container::value, + int> = 0> +bool contains(Range const& range, Key const& key) +#endif +{ + return range.find(key) != range.end(); +} + +#if defined(__SUNPRO_CC) +template +bool contains(Range const& range, Key const& key, detail::overload_selector<0>) +#else +template < + typename Range, typename Key, + cm::enable_if_t::value && + !(cm::is_associative_container::value || + cm::is_unordered_associative_container::value), + int> = 0> +bool contains(Range const& range, Key const& key) +#endif +{ + return std::find(std::begin(range), std::end(range), key) != std::end(range); +} + +#if defined(__SUNPRO_CC) +template +bool contains(Range const& range, Key const& key) +{ + return contains(range, key, detail::overload_selector<1>{}); +} +#endif + } // namespace cm #endif diff --git a/Utilities/std/cmext/iterator b/Utilities/std/cmext/iterator index ffe94b17d51..ce9462f0a59 100644 --- a/Utilities/std/cmext/iterator +++ b/Utilities/std/cmext/iterator @@ -23,25 +23,27 @@ using is_input_iterator = std::is_base_of::iterator_category>; -// checks if a type is a range type: must have a difference_type type +// checks if a type is a range type: std::begin() and std::end() are supported template using is_range = cm::bool_constant< - cm::is_iterator().begin())>::value && - cm::is_iterator().end())>::value>; + cm::is_iterator()))>::value && + cm::is_iterator()))>::value>; -// checks if a type is an input range type: must have methods begin() and end() +// checks if a type is an input range type: std::begin() and std::end() are // returning an input iterator template using is_input_range = #if defined(_MSC_VER) && _MSC_VER < 1920 // MS C++ is not able to evaluate complex type introspection, // so use a simplified version - cm::is_input_iterator; + cm::bool_constant::value || + std::is_pointer::value || + std::is_array::value>; #else - cm::bool_constant< - cm::is_input_iterator().begin())>::value && - cm::is_input_iterator().end())>::value>; + cm::bool_constant()))>::value && + cm::is_input_iterator()))>::value>; #endif } // namespace cm diff --git a/Utilities/std/cmext/memory b/Utilities/std/cmext/memory index 50e79dfe250..fa326f0e381 100644 --- a/Utilities/std/cmext/memory +++ b/Utilities/std/cmext/memory @@ -12,18 +12,19 @@ namespace cm { -template >::value, - int> = 0> +template < + typename T, typename O, + cm::enable_if_t().get())>::value, + int> = 0> T& static_reference_cast(O& item) { return *(static_cast(item.get())); } -template >::value, - int> = 0> + +template < + typename T, typename O, + cm::enable_if_t().get())>::value, + int> = 0> T& dynamic_reference_cast(O& item) { auto p = dynamic_cast(item.get()); diff --git a/Source/cm_static_string_view.hxx b/Utilities/std/cmext/string_view similarity index 89% rename from Source/cm_static_string_view.hxx rename to Utilities/std/cmext/string_view index 708ac9579b7..ad52b115a08 100644 --- a/Source/cm_static_string_view.hxx +++ b/Utilities/std/cmext/string_view @@ -1,9 +1,10 @@ +// -*-c++-*- +// vim: set ft=cpp: + /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cm_static_string_view_hxx -#define cm_static_string_view_hxx - -#include "cmConfigure.h" // IWYU pragma: keep +#ifndef cmext_string_view +#define cmext_string_view #include diff --git a/Utilities/std/cmext/type_traits b/Utilities/std/cmext/type_traits index 00984cba29b..f02b4884e74 100644 --- a/Utilities/std/cmext/type_traits +++ b/Utilities/std/cmext/type_traits @@ -6,6 +6,8 @@ #ifndef cmext_type_traits #define cmext_type_traits +#include + #include namespace cm { diff --git a/bootstrap b/bootstrap index 69b43b16f7d..ad55ac05b92 100755 --- a/bootstrap +++ b/bootstrap @@ -9,15 +9,23 @@ die() { # Compile flag extraction function. cmake_extract_standard_flags() { - sed -n "s/ *set *( *CMAKE_${2}${3}_EXTENSION_COMPILE_OPTION *\"\{0,1\}\([^\")]*\).*/\1/p" \ - "${cmake_source_dir}/Modules/Compiler/"${1:-*}-${2}.cmake 2>/dev/null | tr ';' ' ' + id="${1:-*}" + lang="${2}" + ver="${3}" + sed -n "s/ *set *( *CMAKE_${lang}${ver}_EXTENSION_COMPILE_OPTION *\"\{0,1\}\([^\")]*\).*/\1/p" \ + "${cmake_source_dir}/Modules/Compiler/"${id}-${lang}.cmake \ + 2>/dev/null | tr ';' ' ' + # Clang's CXX compiler flags are in the common module. + sed -n "s/ *set *( *CMAKE_\\\${lang}${ver}_EXTENSION_COMPILE_OPTION *\"\{0,1\}\([^\")]*\).*/\1/p" \ + "${cmake_source_dir}/Modules/Compiler/Clang.cmake" \ + 2>/dev/null | tr ';' ' ' } # Version number extraction function. cmake_version_component() { sed -n " -/^set(CMake_VERSION_${1}/ {s/set(CMake_VERSION_${1} *\([0-9]*\))/\1/;p;} +/^set(CMake_VERSION_${1}/ {s/set(CMake_VERSION_${1} *\([0-9]*\)).*/\1/;p;} " "${cmake_source_dir}/Source/CMakeVersion.cmake" } @@ -38,7 +46,7 @@ cmake_install_dest_default() cmake_toupper() { - echo "$1" | tr '[:lower:]' '[:upper:]' + echo "$1" | tr '[a-z]' '[A-Z]' } # Detect system and directory information. @@ -52,7 +60,7 @@ cmake_version_minor="`cmake_version_component MINOR`" cmake_version_patch="`cmake_version_component PATCH`" cmake_version="${cmake_version_major}.${cmake_version_minor}.${cmake_version_patch}" cmake_version_rc="`cmake_version_component RC`" -if [ "$cmake_version_rc" != "" ]; then +if test "$cmake_version_rc" != ""; then cmake_version="${cmake_version}-rc${cmake_version_rc}" fi @@ -131,7 +139,7 @@ fi if echo "${cmake_system}" | grep HP-UX >/dev/null 2>&1; then die 'CMake no longer compiles on HP-UX. See - https://gitlab.kitware.com/cmake/cmake/issues/17137 + https://gitlab.kitware.com/cmake/cmake/-/issues/17137 Use CMake 3.9 or lower instead.' cmake_system_hpux=true @@ -201,13 +209,13 @@ esac # Choose the default install prefix. if ${cmake_system_mingw}; then - if [ "x${PROGRAMFILES}" != "x" ]; then + if test "x${PROGRAMFILES}" != "x"; then cmake_default_prefix=`cmake_fix_slashes "${PROGRAMFILES}/CMake"` - elif [ "x${ProgramFiles}" != "x" ]; then + elif test "x${ProgramFiles}" != "x"; then cmake_default_prefix=`cmake_fix_slashes "${ProgramFiles}/CMake"` - elif [ "x${SYSTEMDRIVE}" != "x" ]; then + elif test "x${SYSTEMDRIVE}" != "x"; then cmake_default_prefix=`cmake_fix_slashes "${SYSTEMDRIVE}/Program Files/CMake"` - elif [ "x${SystemDrive}" != "x" ]; then + elif test "x${SystemDrive}" != "x"; then cmake_default_prefix=`cmake_fix_slashes "${SystemDrive}/Program Files/CMake"` else cmake_default_prefix="c:/Program Files/CMake" @@ -239,8 +247,12 @@ CMAKE_PROBLEMATIC_FILES="\ */CMakeCXXCompiler.cmake \ Source/cmConfigure.h \ Source/CTest/Curl/config.h \ - Utilities/cmexpat/expatConfig.h \ - Utilities/cmexpat/expatDllConfig.h \ + Utilities/cmThirdParty.h \ + Utilities/cmcurl/lib/curl_config.h \ + Utilities/cmlibarchive/config.h \ + Utilities/cmliblzma/config.h \ + Utilities/cmnghttp2/config.h \ + Utilities/cmzlib/zlibDllConfig.h \ " CMAKE_UNUSED_SOURCES="\ @@ -404,7 +416,6 @@ CMAKE_CXX_SOURCES="\ cmProcessOutput \ cmProjectCommand \ cmPropertyDefinition \ - cmPropertyDefinitionMap \ cmPropertyMap \ cmReturnCommand \ cmRulePlaceholderExpander \ @@ -612,6 +623,8 @@ Configuration: --no-system-bzip2 use cmake-provided bzip2 library (default) --system-liblzma use system-installed liblzma library --no-system-liblzma use cmake-provided liblzma library (default) + --system-nghttp2 use system-installed nghttp2 library + --no-system-nghttp2 use cmake-provided nghttp2 library (default) --system-zstd use system-installed zstd library --no-system-zstd use cmake-provided zstd library (default) --system-libarchive use system-installed libarchive library @@ -664,7 +677,7 @@ cmake_error() echo "Error when bootstrapping CMake:" echo "$*" echo "---------------------------------------------" - if [ -f cmake_bootstrap.log ]; then + if test -f cmake_bootstrap.log; then echo "Log of errors: `pwd`/cmake_bootstrap.log" #cat cmake_bootstrap.log echo "---------------------------------------------" @@ -691,9 +704,9 @@ cmake_replace_string () OUTFILE="$2" SEARCHFOR="$3" REPLACEWITH="$4" - if [ -f "${INFILE}" ] || ${cmake_system_openvms}; then + if test -f "${INFILE}" || ${cmake_system_openvms}; then sed "s/\@${SEARCHFOR}\@/${REPLACEWITH}/g" "${INFILE}" > "${OUTFILE}${_tmp}" - if [ -f "${OUTFILE}${_tmp}" ]; then + if test -f "${OUTFILE}${_tmp}"; then if "${_diff}" "${OUTFILE}" "${OUTFILE}${_tmp}" > /dev/null 2> /dev/null ; then #echo "Files are the same" rm -f "${OUTFILE}${_tmp}" @@ -712,7 +725,7 @@ cmake_kwsys_config_replace_string () OUTFILE="$2" shift 2 APPEND="$*" - if [ -f "${INFILE}" ] || ${cmake_system_openvms}; then + if test -f "${INFILE}" || ${cmake_system_openvms}; then echo "${APPEND}" > "${OUTFILE}${_tmp}" sed "/./ {s/\@KWSYS_NAMESPACE\@/cmsys/g; s/@KWSYS_BUILD_SHARED@/${KWSYS_BUILD_SHARED}/g; @@ -723,7 +736,7 @@ cmake_kwsys_config_replace_string () s/@KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H@/${KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H}/g; s/@KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP@/${KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP}/g; }" "${INFILE}" >> "${OUTFILE}${_tmp}" - if [ -f "${OUTFILE}${_tmp}" ]; then + if test -f "${OUTFILE}${_tmp}"; then if "${_diff}" "${OUTFILE}" "${OUTFILE}${_tmp}" > /dev/null 2> /dev/null ; then #echo "Files are the same" rm -f "${OUTFILE}${_tmp}" @@ -780,7 +793,7 @@ cmake_try_run () COMPILER=$1 FLAGS=$2 TESTFILE=$3 - if [ ! -f "${TESTFILE}" ]; then + if test ! -f "${TESTFILE}"; then echo "Test file ${TESTFILE} missing. Please verify your CMake source tree." exit 4 fi @@ -792,18 +805,18 @@ cmake_try_run () echo "------------------------------------------" "${COMPILER}" ${FLAGS} "${TESTFILE}" -o "${TMPFILE}" RES=$? - if [ "${RES}" -ne "0" ]; then + if test "${RES}" -ne "0"; then echo "Test failed to compile" return 1 fi - if [ ! -f "${TMPFILE}" ] && [ ! -f "${TMPFILE}.exe" ]; then + if test ! -f "${TMPFILE}" && test ! -f "${TMPFILE}.exe"; then echo "Test failed to produce executable" return 2 fi ./${TMPFILE} RES=$? rm -f "${TMPFILE}" - if [ "${RES}" -ne "0" ]; then + if test "${RES}" -ne "0"; then echo "Test produced non-zero return code" return 3 fi @@ -819,18 +832,18 @@ cmake_try_make () echo "Try: ${MAKE_PROC}" "${MAKE_PROC}" ${MAKE_FLAGS} RES=$? - if [ "${RES}" -ne "0" ]; then + if test "${RES}" -ne "0"; then echo "${MAKE_PROC} does not work" return 1 fi - if [ ! -f "test" ] && [ ! -f "test.exe" ]; then + if test ! -f "test" && test ! -f "test.exe"; then echo "${COMPILER} does not produce output" return 2 fi ./test RES=$? rm -f "test" - if [ "${RES}" -ne "0" ]; then + if test "${RES}" -ne "0"; then echo "${MAKE_PROC} produces strange executable" return 3 fi @@ -856,10 +869,10 @@ while test $# != 0; do --init=*) cmake_init_file=`cmake_arg "$1"` ;; --system-libs) cmake_bootstrap_system_libs="${cmake_bootstrap_system_libs} -DCMAKE_USE_SYSTEM_LIBRARIES=1" ;; --no-system-libs) cmake_bootstrap_system_libs="${cmake_bootstrap_system_libs} -DCMAKE_USE_SYSTEM_LIBRARIES=0" ;; - --system-bzip2|--system-curl|--system-expat|--system-jsoncpp|--system-libarchive|--system-librhash|--system-zlib|--system-liblzma|--system-zstd|--system-libuv) + --system-bzip2|--system-curl|--system-expat|--system-jsoncpp|--system-libarchive|--system-librhash|--system-zlib|--system-liblzma|--system-nghttp2|--system-zstd|--system-libuv) lib=`cmake_arg "$1" "--system-"` cmake_bootstrap_system_libs="${cmake_bootstrap_system_libs} -DCMAKE_USE_SYSTEM_LIBRARY_`cmake_toupper $lib`=1" ;; - --no-system-bzip2|--no-system-curl|--no-system-expat|--no-system-jsoncpp|--no-system-libarchive|--no-system-librhash|--no-system-zlib|--no-system-liblzma|--no-system-zstd|--no-system-libuv) + --no-system-bzip2|--no-system-curl|--no-system-expat|--no-system-jsoncpp|--no-system-libarchive|--no-system-librhash|--no-system-zlib|--no-system-liblzma|--no-system-nghttp2|--no-system-zstd|--no-system-libuv) lib=`cmake_arg "$1" "--no-system-"` cmake_bootstrap_system_libs="${cmake_bootstrap_system_libs} -DCMAKE_USE_SYSTEM_LIBRARY_`cmake_toupper $lib`=0" ;; --qt-gui) cmake_bootstrap_qt_gui="1" ;; @@ -887,13 +900,13 @@ while test $# != 0; do done # If verbose, display some information about bootstrap -if [ -n "${cmake_verbose}" ]; then +if test -n "${cmake_verbose}"; then echo "---------------------------------------------" echo "Source directory: ${cmake_source_dir}" echo "Binary directory: ${cmake_binary_dir}" echo "Prefix directory: ${cmake_prefix_dir}" echo "System: ${cmake_system}" - if [ "x${cmake_parallel_make}" != "x" ]; then + if test "x${cmake_parallel_make}" != "x"; then echo "Doing parallel make: ${cmake_parallel_make}" fi echo "" @@ -905,18 +918,18 @@ echo "`cmake_version_display`" # Check for in-source build cmake_in_source_build= -if [ -f "${cmake_binary_dir}/Source/cmake.cxx" -a \ - -f "${cmake_binary_dir}/Source/cmake.h" ]; then - if [ -n "${cmake_verbose}" ]; then +if test -f "${cmake_binary_dir}/Source/cmake.cxx" && + test -f "${cmake_binary_dir}/Source/cmake.h"; then + if test -n "${cmake_verbose}"; then echo "Warning: This is an in-source build" fi cmake_in_source_build=TRUE fi # If this is not an in-source build, then Bootstrap stuff should not exist. -if [ -z "${cmake_in_source_build}" ]; then +if test -z "${cmake_in_source_build}"; then # Did somebody bootstrap in the source tree? - if [ -d "${cmake_source_dir}/Bootstrap${_cmk}" ]; then + if test -d "${cmake_source_dir}/Bootstrap${_cmk}"; then cmake_error 10 "Found directory \"${cmake_source_dir}/Bootstrap${_cmk}\". Looks like somebody did bootstrap CMake in the source tree, but now you are trying to do bootstrap in the binary tree. Please remove Bootstrap${_cmk} @@ -924,7 +937,7 @@ directory from the source tree." fi # Is there a cache in the source tree? for cmake_problematic_file in ${CMAKE_PROBLEMATIC_FILES}; do - if [ -f "${cmake_source_dir}/${cmake_problematic_file}" ]; then + if test -f "${cmake_source_dir}/${cmake_problematic_file}"; then cmake_error 10 "Found \"${cmake_source_dir}/${cmake_problematic_file}\". Looks like somebody tried to build CMake in the source tree, but now you are trying to do bootstrap in the binary tree. Please remove \"${cmake_problematic_file}\" @@ -934,14 +947,14 @@ from the source tree." fi # Make bootstrap directory -[ -d "${cmake_bootstrap_dir}" ] || mkdir "${cmake_bootstrap_dir}" -if [ ! -d "${cmake_bootstrap_dir}" ]; then +test -d "${cmake_bootstrap_dir}" || mkdir "${cmake_bootstrap_dir}" +if test ! -d "${cmake_bootstrap_dir}"; then cmake_error 3 "Cannot create directory ${cmake_bootstrap_dir} to bootstrap CMake." fi cd "${cmake_bootstrap_dir}" -[ -d "cmsys" ] || mkdir "cmsys" -if [ ! -d "cmsys" ]; then +test -d "cmsys" || mkdir "cmsys" +if test ! -d "cmsys"; then cmake_error 4 "Cannot create directory ${cmake_bootstrap_dir}/cmsys" fi @@ -952,7 +965,7 @@ rm -f "${cmake_bootstrap_dir}/cmVersionConfig.h${_tmp}" # If building in-source, remove any cmConfigure.h that may # have been created by a previous run of the bootstrap cmake. -if [ -n "${cmake_in_source_build}" ]; then +if test -n "${cmake_in_source_build}"; then rm -f "${cmake_source_dir}/Source/cmConfigure.h" fi @@ -1037,7 +1050,7 @@ cmake_toolchain_detect() done } -if [ -z "${CC}" -a -z "${CXX}" ]; then +if test -z "${CC}" && test -z "${CXX}"; then cmake_toolchain_detect fi @@ -1051,9 +1064,9 @@ esac cmake_c_compiler= # If CC is set, use that for compiler, otherwise use list of known compilers -if [ -n "${cmake_toolchain}" ]; then +if test -n "${cmake_toolchain}"; then eval cmake_c_compilers="\${cmake_toolchain_${cmake_toolchain}_CC}" -elif [ -n "${CC}" ]; then +elif test -n "${CC}"; then cmake_c_compilers="${CC}" else cmake_c_compilers="${CMAKE_KNOWN_C_COMPILERS}" @@ -1104,7 +1117,7 @@ for std in 11 99 90; do done rm -f "${TMPFILE}.c" -if [ -z "${cmake_c_compiler}" ]; then +if test -z "${cmake_c_compiler}"; then cmake_error 6 "Cannot find appropriate C compiler on this system. Please specify one using environment variable CC. See cmake_bootstrap.log for compilers attempted. @@ -1119,9 +1132,9 @@ cmake_cxx_compiler= # On Mac OSX, CC is the same as cc, so make sure not to try CC as c++ compiler. # If CC is set, use that for compiler, otherwise use list of known compilers -if [ -n "${cmake_toolchain}" ]; then +if test -n "${cmake_toolchain}"; then eval cmake_cxx_compilers="\${cmake_toolchain_${cmake_toolchain}_CXX}" -elif [ -n "${CXX}" ]; then +elif test -n "${CXX}"; then cmake_cxx_compilers="${CXX}" else cmake_cxx_compilers="${CMAKE_KNOWN_CXX_COMPILERS}" @@ -1169,7 +1182,7 @@ int check_cxx14() } #endif -#if (__cplusplus >= 201703L || defined(__INTEL_COMPILER) && defined(__cpp_if_constexpr)) +#if (__cplusplus >= 201703L || defined(__INTEL_COMPILER) && defined(__cpp_deduction_guides)) #include template ::type = nullptr> @@ -1225,7 +1238,7 @@ for std in 17 14 11; do done rm -f "${TMPFILE}.cxx" -if [ -z "${cmake_cxx_compiler}" ]; then +if test -z "${cmake_cxx_compiler}"; then cmake_error 7 "Cannot find a C++ compiler that supports both C++11 and the specified C++ flags. Please specify one using environment variable CXX. The C++ flags are \"$cmake_cxx_flags\". @@ -1252,7 +1265,7 @@ cmake_have_cxx_features="" for feature in ${cmake_cxx_features}; do feature_variable="cmake_have_cxx_${feature}" eval "feature_value=\${${feature_variable}}" - if [ "${feature_value}" -eq "1" ]; then + if test "${feature_value}" -eq "1"; then cmake_have_cxx_features="${cmake_have_cxx_features} -DCMake_HAVE_CXX_`cmake_toupper ${feature}`=${feature_value}" fi done @@ -1264,7 +1277,7 @@ cmake_make_processor= cmake_make_flags= # If MAKE is set, use that for make processor, otherwise use list of known make -if [ -n "${MAKE}" ]; then +if test -n "${MAKE}"; then cmake_make_processors="${MAKE}" else cmake_make_processors="${CMAKE_KNOWN_MAKE_PROCESSORS}" @@ -1283,20 +1296,20 @@ echo ' int main(){ printf("1%c", (char)0x0a); return 0; } ' > "test.c" cmake_original_make_flags="${cmake_make_flags}" -if [ "x${cmake_parallel_make}" != "x" ]; then +if test "x${cmake_parallel_make}" != "x"; then cmake_make_flags="${cmake_make_flags} -j ${cmake_parallel_make}" fi for a in ${cmake_make_processors}; do - if [ -z "${cmake_make_processor}" ] && cmake_try_make "${a}" "${cmake_make_flags}" >> ../cmake_bootstrap.log 2>&1; then + if test -z "${cmake_make_processor}" && cmake_try_make "${a}" "${cmake_make_flags}" >> ../cmake_bootstrap.log 2>&1; then cmake_make_processor="${a}" fi done cmake_full_make_flags="${cmake_make_flags}" -if [ "x${cmake_original_make_flags}" != "x${cmake_make_flags}" ]; then - if [ -z "${cmake_make_processor}" ]; then +if test "x${cmake_original_make_flags}" != "x${cmake_make_flags}"; then + if test -z "${cmake_make_processor}"; then cmake_make_flags="${cmake_original_make_flags}" for a in ${cmake_make_processors}; do - if [ -z "${cmake_make_processor}" ] && cmake_try_make "${a}" "${cmake_make_flags}" >> ../cmake_bootstrap.log 2>&1; then + if test -z "${cmake_make_processor}" && cmake_try_make "${a}" "${cmake_make_flags}" >> ../cmake_bootstrap.log 2>&1; then cmake_make_processor="${a}" fi done @@ -1304,13 +1317,13 @@ if [ "x${cmake_original_make_flags}" != "x${cmake_make_flags}" ]; then fi cd "${cmake_bootstrap_dir}" -if [ -z "${cmake_make_processor}" ]; then +if test -z "${cmake_make_processor}"; then cmake_error 8 "Cannot find appropriate Makefile processor on this system. Please specify one using environment variable MAKE." fi rm -rf "${cmake_bootstrap_dir}/${TMPFILE}" echo "Makefile processor on this system is: ${cmake_make_processor}" -if [ "x${cmake_full_make_flags}" != "x${cmake_make_flags}" ]; then +if test "x${cmake_full_make_flags}" != "x${cmake_make_flags}"; then echo "---------------------------------------------" echo "Makefile processor ${cmake_make_processor} does not support parallel build" echo "---------------------------------------------" @@ -1375,7 +1388,7 @@ else echo "${cmake_cxx_compiler} does not have " fi -if [ -n "${cmake_ccache_enabled}" ]; then +if test -n "${cmake_ccache_enabled}"; then echo "Building CMake with ccache" cmake_c_compiler="ccache ${cmake_c_compiler}" cmake_cxx_compiler="ccache ${cmake_cxx_compiler}" @@ -1525,15 +1538,15 @@ else fi uv_c_flags="${uv_c_flags} `cmake_escape "-I${cmake_source_dir}/Utilities/cmlibuv/src"`" -if [ "x${cmake_ansi_cxx_flags}" != "x" ]; then +if test "x${cmake_ansi_cxx_flags}" != "x"; then cmake_cxx_flags="${cmake_ansi_cxx_flags} ${cmake_cxx_flags}" fi -if [ "x${cmake_c_flags}" != "x" ]; then +if test "x${cmake_c_flags}" != "x"; then cmake_c_flags="${cmake_c_flags} " fi -if [ "x${cmake_cxx_flags}" != "x" ]; then +if test "x${cmake_cxx_flags}" != "x"; then cmake_cxx_flags="${cmake_cxx_flags} " fi @@ -1630,42 +1643,42 @@ set (CMAKE_XDGDATA_DIR "'"${cmake_xdgdata_dir}"'" CACHE PATH "Install location f ' > "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" # Add configuration settings given as command-line options. -if [ "x${cmake_bootstrap_qt_gui}" != "x" ]; then +if test "x${cmake_bootstrap_qt_gui}" != "x"; then echo ' set (BUILD_QtDialog '"${cmake_bootstrap_qt_gui}"' CACHE BOOL "Build Qt dialog for CMake" FORCE) ' >> "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" fi -if [ "x${cmake_bootstrap_qt_qmake}" != "x" ]; then +if test "x${cmake_bootstrap_qt_qmake}" != "x"; then echo ' set (QT_QMAKE_EXECUTABLE "'"${cmake_bootstrap_qt_qmake}"'" CACHE FILEPATH "Location of Qt qmake" FORCE) ' >> "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" fi -if [ "x${cmake_sphinx_info}" != "x" ]; then +if test "x${cmake_sphinx_info}" != "x"; then echo ' set (SPHINX_INFO "'"${cmake_sphinx_info}"'" CACHE BOOL "Build Info manual with Sphinx" FORCE) ' >> "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" fi -if [ "x${cmake_sphinx_man}" != "x" ]; then +if test "x${cmake_sphinx_man}" != "x"; then echo ' set (SPHINX_MAN "'"${cmake_sphinx_man}"'" CACHE BOOL "Build man pages with Sphinx" FORCE) ' >> "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" fi -if [ "x${cmake_sphinx_html}" != "x" ]; then +if test "x${cmake_sphinx_html}" != "x"; then echo ' set (SPHINX_HTML "'"${cmake_sphinx_html}"'" CACHE BOOL "Build html help with Sphinx" FORCE) ' >> "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" fi -if [ "x${cmake_sphinx_qthelp}" != "x" ]; then +if test "x${cmake_sphinx_qthelp}" != "x"; then echo ' set (SPHINX_QTHELP "'"${cmake_sphinx_qthelp}"'" CACHE BOOL "Build qch help with Sphinx" FORCE) ' >> "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" fi -if [ "x${cmake_sphinx_build}" != "x" ]; then +if test "x${cmake_sphinx_build}" != "x"; then echo ' set (SPHINX_EXECUTABLE "'"${cmake_sphinx_build}"'" CACHE FILEPATH "Location of Qt sphinx-build" FORCE) ' >> "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" fi -if [ "x${cmake_sphinx_flags}" != "x" ]; then +if test "x${cmake_sphinx_flags}" != "x"; then echo ' set (SPHINX_FLAGS [==['"${cmake_sphinx_flags}"']==] CACHE STRING "Flags to pass to sphinx-build" FORCE) ' >> "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" @@ -1675,7 +1688,7 @@ fi # specification of cmake_init_file. ( cd "${cmake_binary_dir}" -if [ -f "${cmake_init_file}" ]; then +if test -f "${cmake_init_file}"; then cat "${cmake_init_file}" >> "${cmake_bootstrap_dir}/InitialCacheFlags.cmake" fi ) @@ -1683,13 +1696,13 @@ fi echo "---------------------------------------------" # Run make to build bootstrap cmake -if [ "x${cmake_parallel_make}" != "x" ]; then +if test "x${cmake_parallel_make}" != "x"; then ${cmake_make_processor} ${cmake_make_flags} else ${cmake_make_processor} fi RES=$? -if [ "${RES}" -ne "0" ]; then +if test "${RES}" -ne "0"; then cmake_error 9 "Problem while running ${cmake_make_processor}" fi cd "${cmake_binary_dir}" @@ -1708,12 +1721,12 @@ export LDFLAGS # Run bootstrap CMake to configure real CMake cmake_options="-DCMAKE_BOOTSTRAP=1" -if [ -n "${cmake_verbose}" ]; then +if test -n "${cmake_verbose}"; then cmake_options="${cmake_options} -DCMAKE_VERBOSE_MAKEFILE=1" fi "${cmake_bootstrap_dir}/cmake" "${cmake_source_dir}" "-C${cmake_bootstrap_dir}/InitialCacheFlags.cmake" "-G${cmake_bootstrap_generator}" ${cmake_options} ${cmake_bootstrap_system_libs} "$@" RES=$? -if [ "${RES}" -ne "0" ]; then +if test "${RES}" -ne "0"; then cmake_error 11 "Problem while running initial CMake" fi