diff options
250 files changed, 5979 insertions, 2489 deletions
diff --git a/Help/cpack_gen/external.rst b/Help/cpack_gen/external.rst index f98e1c9..e4912a4 100644 --- a/Help/cpack_gen/external.rst +++ b/Help/cpack_gen/external.rst @@ -21,11 +21,11 @@ install and package files as required. Alternatively CPack can invoke an external packaging software through an optional custom CMake script in -:variable:`CPACK_EXT_PACKAGE_SCRIPT` instead. +:variable:`CPACK_EXTERNAL_PACKAGE_SCRIPT` instead. Staging of installation files may also optionally be taken care of by the generator when enabled through the -:variable:`CPACK_EXT_ENABLE_STAGING` variable. +:variable:`CPACK_EXTERNAL_ENABLE_STAGING` variable. JSON Format ^^^^^^^^^^^ @@ -46,10 +46,10 @@ always of the format ``major.minor``. In other words, it always has exactly two parts, separated by a period. You can request one or more specific versions of the output format as described -below with :variable:`CPACK_EXT_REQUESTED_VERSIONS`. The output format will +below with :variable:`CPACK_EXTERNAL_REQUESTED_VERSIONS`. The output format will have a major version that exactly matches the requested major version, and a minor version that is greater than or equal to the requested minor version. If -no version is requested with :variable:`CPACK_EXT_REQUESTED_VERSIONS`, the +no version is requested with :variable:`CPACK_EXTERNAL_REQUESTED_VERSIONS`, the latest known major version is used by default. Currently, the only supported format is 1.0, which is described below. @@ -234,7 +234,7 @@ following fields in the root: Variables specific to CPack External generator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. variable:: CPACK_EXT_REQUESTED_VERSIONS +.. variable:: CPACK_EXTERNAL_REQUESTED_VERSIONS This variable is used to request a specific version of the CPack External generator. It is a list of ``major.minor`` values, separated by semicolons. @@ -248,7 +248,7 @@ Variables specific to CPack External generator The generator knows how to generate the version if it has a versioned generator whose major version exactly matches the requested major version, and whose minor version is greater than or equal to the requested minor - version. For example, if ``CPACK_EXT_REQUESTED_VERSIONS`` contains 1.0, and + version. For example, if ``CPACK_EXTERNAL_REQUESTED_VERSIONS`` contains 1.0, and the CPack External generator knows how to generate 1.1, it will generate 1.1. If the generator doesn't know how to generate a version in the list, it skips the version and looks at the next one. If it doesn't know how to generate any @@ -257,11 +257,11 @@ Variables specific to CPack External generator If this variable is not set, or is empty, the CPack External generator will generate the highest major and minor version that it knows how to generate. - If an invalid version is encountered in ``CPACK_EXT_REQUESTED_VERSIONS`` (one + If an invalid version is encountered in ``CPACK_EXTERNAL_REQUESTED_VERSIONS`` (one that doesn't match ``major.minor``, where ``major`` and ``minor`` are integers), it is ignored. -.. variable:: CPACK_EXT_ENABLE_STAGING +.. variable:: CPACK_EXTERNAL_ENABLE_STAGING This variable can be set to true to enable optional installation into a temporary staging area which can then be picked up @@ -274,7 +274,7 @@ Variables specific to CPack External generator It also contains the staging area ``CPACK_TEMPORARY_DIRECTORY`` into which CPack performs the installation when staging is enabled. -.. variable:: CPACK_EXT_PACKAGE_SCRIPT +.. variable:: CPACK_EXTERNAL_PACKAGE_SCRIPT This variable can optionally specify the full path to a CMake script file to be run as part of the CPack invocation. diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 1651114..047859d 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -124,6 +124,7 @@ Properties on Targets /prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG /prop_tgt/ARCHIVE_OUTPUT_NAME /prop_tgt/AUTOGEN_BUILD_DIR + /prop_tgt/AUTOGEN_ORIGIN_DEPENDS /prop_tgt/AUTOGEN_PARALLEL /prop_tgt/AUTOGEN_TARGET_DEPENDS /prop_tgt/AUTOMOC_COMPILER_PREDEFINES diff --git a/Help/manual/cmake-qt.7.rst b/Help/manual/cmake-qt.7.rst index 724d8ec..0382794 100644 --- a/Help/manual/cmake-qt.7.rst +++ b/Help/manual/cmake-qt.7.rst @@ -44,14 +44,10 @@ Qt Build Tools Qt relies on some bundled tools for code generation, such as ``moc`` for meta-object code generation, ``uic`` for widget layout and population, -and ``rcc`` for virtual filesystem content generation. These tools may be +and ``rcc`` for virtual file system content generation. These tools may be automatically invoked by :manual:`cmake(1)` if the appropriate conditions are met. The automatic tool invocation may be used with both Qt 4 and Qt 5. -The tools are executed as part of a synthesized custom target generated by -CMake. Target dependencies may be added to that custom target by adding them -to the :prop_tgt:`AUTOGEN_TARGET_DEPENDS` target property. - AUTOMOC ^^^^^^^ @@ -214,19 +210,31 @@ overrides options from the :prop_tgt:`AUTORCC_OPTIONS` target property. Source files can be excluded from :prop_tgt:`AUTORCC` processing by enabling :prop_sf:`SKIP_AUTORCC` or the broader :prop_sf:`SKIP_AUTOGEN`. +The ``<ORIGIN>_autogen`` target +=============================== + +The ``moc`` and ``uic`` tools are executed as part of a synthesized +``<ORIGIN>_autogen`` :command:`custom target <add_custom_target>` generated by +CMake. By default that ``<ORIGIN>_autogen`` target inherits the dependencies +of the ``<ORIGIN>`` target (see :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS`). +Target dependencies may be added to the ``<ORIGIN>_autogen`` target by adding +them to the :prop_tgt:`AUTOGEN_TARGET_DEPENDS` target property. + Visual Studio Generators ======================== -When using the :manual:`Visual Studio generators <cmake-generators(7)>`, -CMake uses a ``PRE_BUILD`` :command:`custom command <add_custom_command>` for -:prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`. -If the :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC` processing depends on files, -a :command:`custom target <add_custom_target>` is used instead. -This happens when - -- The origin target depends on :prop_sf:`GENERATED` files which aren't excluded - from :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC` by :prop_sf:`SKIP_AUTOMOC`, - :prop_sf:`SKIP_AUTOUIC`, :prop_sf:`SKIP_AUTOGEN` or :policy:`CMP0071` +When using the :manual:`Visual Studio generators <cmake-generators(7)>`, CMake +generates a ``PRE_BUILD`` :command:`custom command <add_custom_command>` +instead of the ``<ORIGIN>_autogen`` :command:`custom target <add_custom_target>` +(for :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`). +This isn't always possible though and +an ``<ORIGIN>_autogen`` :command:`custom target <add_custom_target>` is used, +when either + +- the ``<ORIGIN>`` target depends on :prop_sf:`GENERATED` files which aren't + excluded from :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC` by + :prop_sf:`SKIP_AUTOMOC`, :prop_sf:`SKIP_AUTOUIC`, :prop_sf:`SKIP_AUTOGEN` + or :policy:`CMP0071` - :prop_tgt:`AUTOGEN_TARGET_DEPENDS` lists a source file qtmain.lib on Windows diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index b88c661..9b12fc5 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -309,6 +309,7 @@ Variables that Control the Build /variable/CMAKE_ANDROID_STL_TYPE /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG + /variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS /variable/CMAKE_AUTOGEN_PARALLEL /variable/CMAKE_AUTOGEN_VERBOSE /variable/CMAKE_AUTOMOC diff --git a/Help/manual/cpack.1.rst b/Help/manual/cpack.1.rst index 87aa38d..6159d7b 100644 --- a/Help/manual/cpack.1.rst +++ b/Help/manual/cpack.1.rst @@ -29,9 +29,10 @@ Options package(s) in that generator's format according to the details provided in the ``CPackConfig.cmake`` configuration file. A generator is responsible for generating the required inputs for a particular package system and invoking - that system's package creation tools. Possible generator names are specified - in the :manual:`Generators <cmake-generators(7)>` section of the manual and + that system's package creation tools. All supported generators are specified + in the :manual:`Generators <cpack-generators(7)>` section of the manual and the ``--help`` option lists the generators supported for the target platform. + If this option is not given, the :variable:`CPACK_GENERATOR` variable determines the default set of generators that will be used. diff --git a/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst b/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst new file mode 100644 index 0000000..f61089a --- /dev/null +++ b/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst @@ -0,0 +1,26 @@ +AUTOGEN_ORIGIN_DEPENDS +---------------------- + +Switch for forwarding origin target dependencies to the corresponding +``_autogen`` target. + +Targets which have their :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC` property +``ON`` have a corresponding ``_autogen`` target which is used to auto generate +``moc`` and ``uic`` files. As this ``_autogen`` target is created at +generate-time, it is not possible to define dependencies of it, +such as to create inputs for the ``moc`` or ``uic`` executable. + +The dependencies of the ``_autogen`` target are composed from + +- the origin target dependencies + (by default enabled via :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS`) +- user defined dependencies from :prop_tgt:`AUTOGEN_TARGET_DEPENDS` + +:prop_tgt:`AUTOGEN_ORIGIN_DEPENDS` decides whether the origin target +dependencies should be forwarded to the ``_autogen`` target or not. + +By default :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS` is initialized from +:variable:`CMAKE_AUTOGEN_ORIGIN_DEPENDS` which is ``ON`` by default. + +See the :manual:`cmake-qt(7)` manual for more information on using CMake +with Qt. diff --git a/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst b/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst index 7d3dfd1..84c2bfe 100644 --- a/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst +++ b/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst @@ -9,9 +9,15 @@ Targets which have their :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC` property generate-time, it is not possible to define dependencies of it, such as to create inputs for the ``moc`` or ``uic`` executable. -The :prop_tgt:`AUTOGEN_TARGET_DEPENDS` target property can be set instead to a -list of dependencies of the ``_autogen`` target. Dependencies can be target -names or file names. +The dependencies of the ``_autogen`` target are composed from + +- the origin target dependencies + (by default enabled via :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS`) +- user defined dependencies from :prop_tgt:`AUTOGEN_TARGET_DEPENDS` + +The :prop_tgt:`AUTOGEN_TARGET_DEPENDS` target property can be set to a +list of additional dependencies for the ``_autogen`` target. Dependencies +can be target names or file names. See the :manual:`cmake-qt(7)` manual for more information on using CMake with Qt. diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst index 3bd693a..70d89f2 100644 --- a/Help/prop_tgt/AUTOMOC.rst +++ b/Help/prop_tgt/AUTOMOC.rst @@ -3,8 +3,8 @@ AUTOMOC Should the target be processed with automoc (for Qt projects). -AUTOMOC is a boolean specifying whether CMake will handle the Qt ``moc`` -preprocessor automatically, i.e. without having to use the +:prop_tgt:`AUTOMOC` is a boolean specifying whether CMake will handle the Qt +``moc`` preprocessor automatically, i.e. without having to use the :module:`QT4_WRAP_CPP() <FindQt4>` or QT5_WRAP_CPP() macro. Currently Qt4 and Qt5 are supported. diff --git a/Help/prop_tgt/AUTORCC.rst b/Help/prop_tgt/AUTORCC.rst index 3cc5990..99c2b0e 100644 --- a/Help/prop_tgt/AUTORCC.rst +++ b/Help/prop_tgt/AUTORCC.rst @@ -3,7 +3,7 @@ AUTORCC Should the target be processed with autorcc (for Qt projects). -``AUTORCC`` is a boolean specifying whether CMake will handle +:prop_tgt:`AUTORCC` is a boolean specifying whether CMake will handle the Qt ``rcc`` code generator automatically, i.e. without having to use the :module:`QT4_ADD_RESOURCES() <FindQt4>` or ``QT5_ADD_RESOURCES()`` macro. Currently Qt4 and Qt5 are supported. diff --git a/Help/prop_tgt/AUTOUIC.rst b/Help/prop_tgt/AUTOUIC.rst index 4fc603f..8cae0a7 100644 --- a/Help/prop_tgt/AUTOUIC.rst +++ b/Help/prop_tgt/AUTOUIC.rst @@ -3,7 +3,7 @@ AUTOUIC Should the target be processed with autouic (for Qt projects). -``AUTOUIC`` is a boolean specifying whether CMake will handle +:prop_tgt:`AUTOUIC` is a boolean specifying whether CMake will handle the Qt ``uic`` code generator automatically, i.e. without having to use the :module:`QT4_WRAP_UI() <FindQt4>` or ``QT5_WRAP_UI()`` macro. Currently Qt4 and Qt5 are supported. diff --git a/Help/release/3.13.rst b/Help/release/3.13.rst index f547556..605122a 100644 --- a/Help/release/3.13.rst +++ b/Help/release/3.13.rst @@ -138,6 +138,9 @@ Properties Modules ------- +* The :module:`FindBoost` module gained a ``Boost_ARCHITECTURE`` option + to specify a Boost architecture-specific library filename fragment. + * The :module:`FindCURL` module learned to find debug and release variants separately. diff --git a/Help/release/dev/FindGDAL-target.rst b/Help/release/dev/FindGDAL-target.rst new file mode 100644 index 0000000..b121a72 --- /dev/null +++ b/Help/release/dev/FindGDAL-target.rst @@ -0,0 +1,4 @@ +FindGDAL-target +--------------- + +* The :module:`FindGDAL` module now provides an imported target. diff --git a/Help/release/dev/autogen-origin-depends.rst b/Help/release/dev/autogen-origin-depends.rst new file mode 100644 index 0000000..7310487 --- /dev/null +++ b/Help/release/dev/autogen-origin-depends.rst @@ -0,0 +1,7 @@ +autogen-origin-depends +---------------------- + +* A new :variable:`CMAKE_AUTOGEN_ORIGIN_DEPENDS` variable and + :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS` target property may be set to enable or + disable forwarding of the origin target dependencies to the corresponding + ``_autogen`` target. diff --git a/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst b/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst new file mode 100644 index 0000000..1398e78 --- /dev/null +++ b/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst @@ -0,0 +1,11 @@ +CMAKE_AUTOGEN_ORIGIN_DEPENDS +---------------------------- + +Switch for forwarding origin target dependencies to the corresponding +``_autogen`` targets. + +This variable is used to initialize the :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS` +property on all the targets. See that target property for additional +information. + +By default :variable:`CMAKE_AUTOGEN_ORIGIN_DEPENDS` is ``ON``. diff --git a/Modules/CMakeASMCompiler.cmake.in b/Modules/CMakeASMCompiler.cmake.in index a6465f6..b8e09fe 100644 --- a/Modules/CMakeASMCompiler.cmake.in +++ b/Modules/CMakeASMCompiler.cmake.in @@ -5,6 +5,7 @@ set(CMAKE_ASM@ASM_DIALECT@_COMPILER_AR "@_CMAKE_ASM_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_RANLIB "@_CMAKE_ASM_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LOADED 1) set(CMAKE_ASM@ASM_DIALECT@_COMPILER_ID "@_CMAKE_ASM_COMPILER_ID@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_VERSION "@_CMAKE_ASM_COMPILER_VERSION@") diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in index 72144cf..e75c74e 100644 --- a/Modules/CMakeCCompiler.cmake.in +++ b/Modules/CMakeCCompiler.cmake.in @@ -21,6 +21,7 @@ set(CMAKE_C_COMPILER_AR "@CMAKE_C_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_C_COMPILER_RANLIB "@CMAKE_C_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@) set(CMAKE_C_COMPILER_LOADED 1) set(CMAKE_C_COMPILER_WORKS @CMAKE_C_COMPILER_WORKS@) diff --git a/Modules/CMakeCUDACompiler.cmake.in b/Modules/CMakeCUDACompiler.cmake.in index 9761d8c..feb3e79 100644 --- a/Modules/CMakeCUDACompiler.cmake.in +++ b/Modules/CMakeCUDACompiler.cmake.in @@ -26,3 +26,6 @@ set(CMAKE_CUDA_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_CUDA_HOST_IMPLIC set(CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES "@CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES@") set(CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES "@CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES@") set(CMAKE_CUDA_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_CUDA_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@") + +set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_MT "@CMAKE_MT@") diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in index 8da6a8c..5f52fd8 100644 --- a/Modules/CMakeCXXCompiler.cmake.in +++ b/Modules/CMakeCXXCompiler.cmake.in @@ -23,6 +23,7 @@ set(CMAKE_CXX_COMPILER_AR "@CMAKE_CXX_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_CXX_COMPILER_RANLIB "@CMAKE_CXX_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@) set(CMAKE_CXX_COMPILER_LOADED 1) set(CMAKE_CXX_COMPILER_WORKS @CMAKE_CXX_COMPILER_WORKS@) diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake index 830639d..35f75c9 100644 --- a/Modules/CMakeFindBinUtils.cmake +++ b/Modules/CMakeFindBinUtils.cmake @@ -14,29 +14,48 @@ # CMAKE_AR # CMAKE_RANLIB # CMAKE_LINKER +# CMAKE_MT # CMAKE_STRIP # CMAKE_INSTALL_NAME_TOOL # on UNIX, cygwin and mingw -if(CMAKE_LINKER) - # we only get here if CMAKE_LINKER was specified using -D or a pre-made CMakeCache.txt - # (e.g. via ctest) or set in CMAKE_TOOLCHAIN_FILE - # find the linker in the PATH if necessary - get_filename_component(_CMAKE_USER_LINKER_PATH "${CMAKE_LINKER}" PATH) - if(NOT _CMAKE_USER_LINKER_PATH) - find_program(CMAKE_LINKER_WITH_PATH NAMES ${CMAKE_LINKER} HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - if(CMAKE_LINKER_WITH_PATH) - set(CMAKE_LINKER ${CMAKE_LINKER_WITH_PATH}) - get_property(_CMAKE_LINKER_CACHED CACHE CMAKE_LINKER PROPERTY TYPE) - if(_CMAKE_LINKER_CACHED) - set(CMAKE_LINKER "${CMAKE_LINKER}" CACHE STRING "Default Linker" FORCE) +# Resolve full path of CMAKE_TOOL from user-defined name and SEARCH_PATH. +function(__resolve_tool_path CMAKE_TOOL SEARCH_PATH DOCSTRING) + + if(${CMAKE_TOOL}) + # We only get here if CMAKE_TOOL was + # specified using -D or a pre-made CMakeCache.txt (e.g. via ctest) + # or set in CMAKE_TOOLCHAIN_FILE. + + get_filename_component(_CMAKE_USER_TOOL_PATH "${${CMAKE_TOOL}}" DIRECTORY) + # Is CMAKE_TOOL a user-defined name instead of a full path? + if(NOT _CMAKE_USER_TOOL_PATH) + + # Find CMAKE_TOOL in the SEARCH_PATH directory by user-defined name. + find_program(_CMAKE_TOOL_WITH_PATH NAMES ${${CMAKE_TOOL}} HINTS ${SEARCH_PATH}) + if(_CMAKE_TOOL_WITH_PATH) + + # Overwrite CMAKE_TOOL with full path found in SEARCH_PATH. + set(${CMAKE_TOOL} ${_CMAKE_TOOL_WITH_PATH} PARENT_SCOPE) + + get_property(_CMAKE_TOOL_CACHED CACHE ${CMAKE_TOOL} PROPERTY TYPE) + # If CMAKE_TOOL is present in the CMake Cache, then overwrit it as well. + if(_CMAKE_TOOL_CACHED) + set(${CMAKE_TOOL} "${_CMAKE_TOOL_WITH_PATH}" CACHE STRING ${DOCSTRING} FORCE) + endif() + endif() - unset(_CMAKE_LINKER_CACHED) + unset(_CMAKE_TOOL_WITH_PATH CACHE) + endif() - unset(CMAKE_LINKER_WITH_PATH CACHE) + endif() -endif() + +endfunction() + +__resolve_tool_path(CMAKE_LINKER "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Linker") +__resolve_tool_path(CMAKE_MT "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Manifest Tool") set(_CMAKE_TOOL_VARS "") @@ -44,12 +63,14 @@ set(_CMAKE_TOOL_VARS "") if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC" OR "x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xMSVC" OR (CMAKE_HOST_WIN32 AND "x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xPGI") + OR (CMAKE_HOST_WIN32 AND "x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xNVIDIA") OR (CMAKE_GENERATOR MATCHES "Visual Studio" AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "Tegra-Android")) find_program(CMAKE_LINKER NAMES link HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) + find_program(CMAKE_MT NAMES mt HINTS ${_CMAKE_TOOLCHAIN_LOCATION}) - list(APPEND _CMAKE_TOOL_VARS CMAKE_LINKER) + list(APPEND _CMAKE_TOOL_VARS CMAKE_LINKER CMAKE_MT) # in all other cases search for ar, ranlib, etc. else() diff --git a/Modules/CMakeGenericSystem.cmake b/Modules/CMakeGenericSystem.cmake index 02cb464..ddfc7bd 100644 --- a/Modules/CMakeGenericSystem.cmake +++ b/Modules/CMakeGenericSystem.cmake @@ -23,6 +23,8 @@ set(CMAKE_DL_LIBS "dl") set(CMAKE_FIND_LIBRARY_PREFIXES "lib") set(CMAKE_FIND_LIBRARY_SUFFIXES ".so" ".a") + +set(CMAKE_AUTOGEN_ORIGIN_DEPENDS ON) set(CMAKE_AUTOMOC_COMPILER_PREDEFINES ON) set(CMAKE_AUTOMOC_MACRO_NAMES "Q_OBJECT" "Q_GADGET" "Q_NAMESPACE") diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake index 48cd207..d150826 100644 --- a/Modules/FindBLAS.cmake +++ b/Modules/FindBLAS.cmake @@ -223,7 +223,7 @@ if (BLA_VENDOR MATCHES "Intel" OR BLA_VENDOR STREQUAL "All") set(BLAS_SEARCH_LIBS "") if(BLA_F95) - set(BLAS_mkl_SEARCH_SYMBOL SGEMM) + set(BLAS_mkl_SEARCH_SYMBOL sgemm_f95) set(_LIBRARIES BLAS95_LIBRARIES) if (WIN32) if (BLA_STATIC) diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake index 4cae8cb..889dbf1 100644 --- a/Modules/FindBoost.cmake +++ b/Modules/FindBoost.cmake @@ -147,6 +147,9 @@ setting variables:: used if multiple compatible suffixes should be tested for, in decreasing order of preference. + Boost_ARCHITECTURE - Set to the architecture-specific library suffix + (e.g. "-x64"). Default is auto-computed for the + C++ compiler in use. Boost_THREADAPI - Suffix for "thread" component library name, such as "pthread" or "win32". Names with and without this suffix will both be tried. @@ -1264,7 +1267,7 @@ if(NOT Boost_INCLUDE_DIR) list(APPEND _boost_INCLUDE_SEARCH_DIRS NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH) else() if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") - foreach(ver ${_Boost_KNOWN_VERSIONS}) + foreach(ver ${_boost_TEST_VERSIONS}) string(REPLACE "." "_" ver "${ver}") list(APPEND _boost_INCLUDE_SEARCH_DIRS PATHS "C:/local/boost_${ver}") endforeach() @@ -1507,27 +1510,35 @@ endif() # -x86 Architecture and address model tag # First character is the architecture, then word-size, either 32 or 64 # Only used in 'versioned' layout, added in Boost 1.66.0 -set(_boost_ARCHITECTURE_TAG "") -# {CMAKE_CXX_COMPILER_ARCHITECTURE_ID} is not currently set for all compilers -if(NOT "x${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "x" AND NOT Boost_VERSION VERSION_LESS 106600) - string(APPEND _boost_ARCHITECTURE_TAG "-") - # This needs to be kept in-sync with the section of CMakePlatformId.h.in - # inside 'defined(_WIN32) && defined(_MSC_VER)' - if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "IA64") - string(APPEND _boost_ARCHITECTURE_TAG "i") - elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "X86" - OR CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "x64") - string(APPEND _boost_ARCHITECTURE_TAG "x") - elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID MATCHES "^ARM") - string(APPEND _boost_ARCHITECTURE_TAG "a") - elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "MIPS") - string(APPEND _boost_ARCHITECTURE_TAG "m") - endif() - - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - string(APPEND _boost_ARCHITECTURE_TAG "64") - else() - string(APPEND _boost_ARCHITECTURE_TAG "32") +if(DEFINED Boost_ARCHITECTURE) + set(_boost_ARCHITECTURE_TAG "${Boost_ARCHITECTURE}") + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "using user-specified Boost_ARCHITECTURE = ${_boost_ARCHITECTURE_TAG}") + endif() +else() + set(_boost_ARCHITECTURE_TAG "") + # {CMAKE_CXX_COMPILER_ARCHITECTURE_ID} is not currently set for all compilers + if(NOT "x${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "x" AND NOT Boost_VERSION VERSION_LESS 106600) + string(APPEND _boost_ARCHITECTURE_TAG "-") + # This needs to be kept in-sync with the section of CMakePlatformId.h.in + # inside 'defined(_WIN32) && defined(_MSC_VER)' + if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "IA64") + string(APPEND _boost_ARCHITECTURE_TAG "i") + elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "X86" + OR CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "x64") + string(APPEND _boost_ARCHITECTURE_TAG "x") + elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID MATCHES "^ARM") + string(APPEND _boost_ARCHITECTURE_TAG "a") + elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "MIPS") + string(APPEND _boost_ARCHITECTURE_TAG "m") + endif() + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + string(APPEND _boost_ARCHITECTURE_TAG "64") + else() + string(APPEND _boost_ARCHITECTURE_TAG "32") + endif() endif() endif() @@ -1582,7 +1593,7 @@ foreach(c DEBUG RELEASE) if( Boost_NO_SYSTEM_PATHS ) list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH) else() - foreach(ver ${_Boost_KNOWN_VERSIONS}) + foreach(ver ${_boost_TEST_VERSIONS}) string(REPLACE "." "_" ver "${ver}") _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "C:/local/boost_${ver}") endforeach() diff --git a/Modules/FindGDAL.cmake b/Modules/FindGDAL.cmake index 030553f..8522f9b 100644 --- a/Modules/FindGDAL.cmake +++ b/Modules/FindGDAL.cmake @@ -5,28 +5,45 @@ FindGDAL -------- +Find GDAL. +IMPORTED Targets +^^^^^^^^^^^^^^^^ -Locate gdal +This module defines :prop_tgt:`IMPORTED` target ``GDAL::GDAL`` +if GDAL has been found. -This module accepts the following environment variables: +Result Variables +^^^^^^^^^^^^^^^^ -:: +This module will set the following variables in your project: - GDAL_DIR or GDAL_ROOT - Specify the location of GDAL +``GDAL_FOUND`` + True if GDAL is found. +``GDAL_INCLUDE_DIRS`` + Include directories for GDAL headers. +``GDAL_LIBRARIES`` + Libraries to link to GDAL. +``GDAL_VERSION`` + The version of GDAL found. +Cache variables +^^^^^^^^^^^^^^^ +The following cache variables may also be set: -This module defines the following CMake variables: +``GDAL_LIBRARY`` + The libgdal library file. +``GDAL_INCLUDE_DIR`` + The directory containing ``gdal.h``. -:: +Hints +^^^^^ - GDAL_FOUND - True if libgdal is found - GDAL_LIBRARY - A variable pointing to the GDAL library - GDAL_INCLUDE_DIR - Where to find the headers +Set ``GDAL_DIR`` or ``GDAL_ROOT`` in the environment to specify the +GDAL installation prefix. #]=======================================================================] -# # $GDALDIR is an environment variable that would # correspond to the ./configure --prefix=$GDAL_DIR # used in building gdal. @@ -123,8 +140,26 @@ find_library(GDAL_LIBRARY PATH_SUFFIXES lib ) +if (EXISTS "${GDAL_INCLUDE_DIR}/gdal_version.h") + file(STRINGS "${GDAL_INCLUDE_DIR}/gdal_version.h" _gdal_version + REGEX "GDAL_RELEASE_NAME") + string(REGEX REPLACE ".*\"\(.*\)\"" "\\1" GDAL_VERSION "${_gdal_version}") + unset(_gdal_version) +else () + set(GDAL_VERSION GDAL_VERSION-NOTFOUND) +endif () + include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(GDAL DEFAULT_MSG GDAL_LIBRARY GDAL_INCLUDE_DIR) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GDAL + VERSION_VAR GDAL_VERSION + REQUIRED_VARS GDAL_LIBRARY GDAL_INCLUDE_DIR) + +if (GDAL_FOUND AND NOT TARGET GDAL::GDAL) + add_library(GDAL::GDAL UNKNOWN IMPORTED) + set_target_properties(GDAL::GDAL PROPERTIES + IMPORTED_LOCATION "${GDAL_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GDAL_INCLUDE_DIR}") +endif () set(GDAL_LIBRARIES ${GDAL_LIBRARY}) set(GDAL_INCLUDE_DIRS ${GDAL_INCLUDE_DIR}) diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake index 31e5620b..62ff94c 100644 --- a/Modules/FindLAPACK.cmake +++ b/Modules/FindLAPACK.cmake @@ -288,7 +288,7 @@ if (BLA_VENDOR MATCHES "Intel" OR BLA_VENDOR STREQUAL "All") set(LAPACK_SEARCH_LIBS "") if (BLA_F95) - set(LAPACK_mkl_SEARCH_SYMBOL "CHEEV") + set(LAPACK_mkl_SEARCH_SYMBOL "cheev_f95") set(_LIBRARIES LAPACK95_LIBRARIES) set(_BLAS_LIBRARIES ${BLAS95_LIBRARIES}) @@ -299,7 +299,7 @@ if (BLA_VENDOR MATCHES "Intel" OR BLA_VENDOR STREQUAL "All") list(APPEND LAPACK_SEARCH_LIBS "mkl_intel_c") list(APPEND LAPACK_SEARCH_LIBS - "mkl_intel_${BLAS_mkl_ILP_MODE}") + "mkl_lapack95_${BLAS_mkl_ILP_MODE}") else() set(LAPACK_mkl_SEARCH_SYMBOL "cheev") set(_LIBRARIES LAPACK_LIBRARIES) diff --git a/Modules/FindProtobuf.cmake b/Modules/FindProtobuf.cmake index 1fc2167..593fff6 100644 --- a/Modules/FindProtobuf.cmake +++ b/Modules/FindProtobuf.cmake @@ -382,21 +382,16 @@ function(_protobuf_find_libraries name filename) mark_as_advanced(${name}_LIBRARY_DEBUG) select_library_configurations(${name}) + + if(UNIX AND Threads_FOUND) + list(APPEND ${name}_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + endif() + set(${name}_LIBRARY "${${name}_LIBRARY}" PARENT_SCOPE) set(${name}_LIBRARIES "${${name}_LIBRARIES}" PARENT_SCOPE) endif() endfunction() -# Internal function: find threads library -function(_protobuf_find_threads) - set(CMAKE_THREAD_PREFER_PTHREAD TRUE) - find_package(Threads) - if(Threads_FOUND) - list(APPEND Protobuf_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) - set(Protobuf_LIBRARIES "${Protobuf_LIBRARIES}" PARENT_SCOPE) - endif() -endfunction() - # # Main. # @@ -417,6 +412,11 @@ if(MSVC) find_path(Protobuf_SRC_ROOT_FOLDER protobuf.pc.in) endif() +if(UNIX) + # Protobuf headers may depend on threading. + find_package(Threads QUIET) +endif() + # The Protobuf library _protobuf_find_libraries(Protobuf protobuf) #DOC "The Google Protocol Buffers RELEASE Library" @@ -431,10 +431,6 @@ if(MSVC) set(CMAKE_FIND_LIBRARY_PREFIXES "${Protobuf_ORIG_FIND_LIBRARY_PREFIXES}") endif() -if(UNIX) - _protobuf_find_threads() -endif() - # Find the include directory find_path(Protobuf_INCLUDE_DIR google/protobuf/service.h @@ -522,6 +518,10 @@ if(Protobuf_INCLUDE_DIR) set_target_properties(protobuf::libprotobuf PROPERTIES IMPORTED_LOCATION_DEBUG "${Protobuf_LIBRARY_DEBUG}") endif() + if(UNIX AND TARGET Threads::Threads) + set_property(TARGET protobuf::libprotobuf APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() endif() endif() @@ -546,6 +546,10 @@ if(Protobuf_INCLUDE_DIR) set_target_properties(protobuf::libprotobuf-lite PROPERTIES IMPORTED_LOCATION_DEBUG "${Protobuf_LITE_LIBRARY_DEBUG}") endif() + if(UNIX AND TARGET Threads::Threads) + set_property(TARGET protobuf::libprotobuf-lite APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() endif() endif() @@ -570,6 +574,10 @@ if(Protobuf_INCLUDE_DIR) set_target_properties(protobuf::libprotoc PROPERTIES IMPORTED_LOCATION_DEBUG "${Protobuf_PROTOC_LIBRARY_DEBUG}") endif() + if(UNIX AND TARGET Threads::Threads) + set_property(TARGET protobuf::libprotoc APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() endif() endif() diff --git a/Modules/Internal/CPack/CPackExt.cmake b/Modules/Internal/CPack/CPackExternal.cmake index e52d978..e4d055a 100644 --- a/Modules/Internal/CPack/CPackExt.cmake +++ b/Modules/Internal/CPack/CPackExternal.cmake @@ -1,15 +1,15 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -if(NOT "${CPACK_EXT_REQUESTED_VERSIONS}" STREQUAL "") +if(NOT "${CPACK_EXTERNAL_REQUESTED_VERSIONS}" STREQUAL "") unset(_found_major) - foreach(_req_version IN LISTS CPACK_EXT_REQUESTED_VERSIONS) + foreach(_req_version IN LISTS CPACK_EXTERNAL_REQUESTED_VERSIONS) if(_req_version MATCHES "^([0-9]+)\\.([0-9]+)$") set(_req_major "${CMAKE_MATCH_1}") set(_req_minor "${CMAKE_MATCH_2}") - foreach(_known_version IN LISTS CPACK_EXT_KNOWN_VERSIONS) + foreach(_known_version IN LISTS CPACK_EXTERNAL_KNOWN_VERSIONS) string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _known_version_dummy @@ -33,21 +33,21 @@ if(NOT "${CPACK_EXT_REQUESTED_VERSIONS}" STREQUAL "") endforeach() if(DEFINED _found_major) - set(CPACK_EXT_SELECTED_MAJOR "${_found_major}") - set(CPACK_EXT_SELECTED_MINOR "${_found_minor}") - set(CPACK_EXT_SELECTED_VERSION "${_found_major}.${_found_minor}") + set(CPACK_EXTERNAL_SELECTED_MAJOR "${_found_major}") + set(CPACK_EXTERNAL_SELECTED_MINOR "${_found_minor}") + set(CPACK_EXTERNAL_SELECTED_VERSION "${_found_major}.${_found_minor}") else() message(FATAL_ERROR - "Could not find a suitable version in CPACK_EXT_REQUESTED_VERSIONS" + "Could not find a suitable version in CPACK_EXTERNAL_REQUESTED_VERSIONS" ) endif() else() - list(GET CPACK_EXT_KNOWN_VERSIONS 0 CPACK_EXT_SELECTED_VERSION) + list(GET CPACK_EXTERNAL_KNOWN_VERSIONS 0 CPACK_EXTERNAL_SELECTED_VERSION) string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _dummy - "${CPACK_EXT_SELECTED_VERSION}" + "${CPACK_EXTERNAL_SELECTED_VERSION}" ) - set(CPACK_EXT_SELECTED_MAJOR "${CMAKE_MATCH_1}") - set(CPACK_EXT_SELECTED_MINOR "${CMAKE_MATCH_2}") + set(CPACK_EXTERNAL_SELECTED_MAJOR "${CMAKE_MATCH_1}") + set(CPACK_EXTERNAL_SELECTED_MINOR "${CMAKE_MATCH_2}") endif() diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index ae180ed..2daf313 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake @@ -298,8 +298,8 @@ unset(_MACHINE_ARCH_FLAG) macro(__windows_compiler_msvc lang) if(NOT MSVC_VERSION LESS 1400) # for 2005 make sure the manifest is put in the dll with mt - set(_CMAKE_VS_LINK_DLL "<CMAKE_COMMAND> -E vs_link_dll --intdir=<OBJECT_DIR> --manifests <MANIFESTS> -- ") - set(_CMAKE_VS_LINK_EXE "<CMAKE_COMMAND> -E vs_link_exe --intdir=<OBJECT_DIR> --manifests <MANIFESTS> -- ") + set(_CMAKE_VS_LINK_DLL "<CMAKE_COMMAND> -E vs_link_dll --intdir=<OBJECT_DIR> --rc=<CMAKE_RC_COMPILER> --mt=<CMAKE_MT> --manifests <MANIFESTS> -- ") + set(_CMAKE_VS_LINK_EXE "<CMAKE_COMMAND> -E vs_link_exe --intdir=<OBJECT_DIR> --rc=<CMAKE_RC_COMPILER> --mt=<CMAKE_MT> --manifests <MANIFESTS> -- ") endif() set(CMAKE_${lang}_CREATE_SHARED_LIBRARY "${_CMAKE_VS_LINK_DLL}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /dll /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}") @@ -366,12 +366,15 @@ macro(__windows_compiler_msvc lang) endif() set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON) set(CMAKE_NINJA_DEPTYPE_${lang} msvc) + __windows_compiler_msvc_enable_rc("${_PLATFORM_DEFINES} ${_PLATFORM_DEFINES_${lang}}") +endmacro() +macro(__windows_compiler_msvc_enable_rc flags) if(NOT CMAKE_RC_COMPILER_INIT) set(CMAKE_RC_COMPILER_INIT rc) endif() if(NOT CMAKE_RC_FLAGS_INIT) - string(APPEND CMAKE_RC_FLAGS_INIT " ${_PLATFORM_DEFINES} ${_PLATFORM_DEFINES_${lang}}") + string(APPEND CMAKE_RC_FLAGS_INIT " ${flags}") endif() if(NOT CMAKE_RC_FLAGS_DEBUG_INIT) string(APPEND CMAKE_RC_FLAGS_DEBUG_INIT " /D_DEBUG") diff --git a/Modules/Platform/Windows-NVIDIA-CUDA.cmake b/Modules/Platform/Windows-NVIDIA-CUDA.cmake index ba1638f..6a2667a 100644 --- a/Modules/Platform/Windows-NVIDIA-CUDA.cmake +++ b/Modules/Platform/Windows-NVIDIA-CUDA.cmake @@ -17,8 +17,8 @@ endforeach() set(CMAKE_CUDA_LINK_EXECUTABLE "<CMAKE_CUDA_HOST_LINK_LAUNCHER> <LINK_FLAGS> <OBJECTS> /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <LINK_LIBRARIES>${__IMPLICT_LINKS}") -set(_CMAKE_VS_LINK_DLL "<CMAKE_COMMAND> -E vs_link_dll --intdir=<OBJECT_DIR> --manifests <MANIFESTS> -- ") -set(_CMAKE_VS_LINK_EXE "<CMAKE_COMMAND> -E vs_link_exe --intdir=<OBJECT_DIR> --manifests <MANIFESTS> -- ") +set(_CMAKE_VS_LINK_DLL "<CMAKE_COMMAND> -E vs_link_dll --intdir=<OBJECT_DIR> --rc=<CMAKE_RC_COMPILER> --mt=<CMAKE_MT> --manifests <MANIFESTS> -- ") +set(_CMAKE_VS_LINK_EXE "<CMAKE_COMMAND> -E vs_link_exe --intdir=<OBJECT_DIR> --rc=<CMAKE_RC_COMPILER> --mt=<CMAKE_MT> --manifests <MANIFESTS> -- ") set(CMAKE_CUDA_CREATE_SHARED_LIBRARY "${_CMAKE_VS_LINK_DLL}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /dll /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES>${__IMPLICT_LINKS} ${CMAKE_END_TEMP_FILE}") @@ -67,3 +67,5 @@ string(APPEND CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT " -Xcompiler=\"-MD -Zi -O2 -O string(APPEND CMAKE_CUDA_FLAGS_MINSIZEREL_INIT " -Xcompiler=\"-MD -O1 -Ob1\" -DNDEBUG") set(CMAKE_CUDA_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT}") + +__windows_compiler_msvc_enable_rc("${_PLATFORM_DEFINES} ${_PLATFORM_DEFINES_CXX}") diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 5e2758a..ded21bc 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -892,7 +892,7 @@ set(CPACK_SRCS CPack/cmCPackArchiveGenerator.cxx CPack/cmCPackComponentGroup.cxx CPack/cmCPackDebGenerator.cxx - CPack/cmCPackExtGenerator.cxx + CPack/cmCPackExternalGenerator.cxx CPack/cmCPackGeneratorFactory.cxx CPack/cmCPackGenerator.cxx CPack/cmCPackLog.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 4481ea4..7918d80 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 13) -set(CMake_VERSION_PATCH 20181031) +set(CMake_VERSION_PATCH 20181106) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/cmCPackExtGenerator.cxx b/Source/CPack/cmCPackExternalGenerator.cxx index 4c560b9..9f7b236 100644 --- a/Source/CPack/cmCPackExtGenerator.cxx +++ b/Source/CPack/cmCPackExternalGenerator.cxx @@ -1,6 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmCPackExtGenerator.h" +#include "cmCPackExternalGenerator.h" #include "cmAlgorithms.h" #include "cmCPackComponentGroup.h" @@ -16,25 +16,25 @@ #include <utility> #include <vector> -int cmCPackExtGenerator::InitializeInternal() +int cmCPackExternalGenerator::InitializeInternal() { - this->SetOption("CPACK_EXT_KNOWN_VERSIONS", "1.0"); + this->SetOption("CPACK_EXTERNAL_KNOWN_VERSIONS", "1.0"); - if (!this->ReadListFile("Internal/CPack/CPackExt.cmake")) { + if (!this->ReadListFile("Internal/CPack/CPackExternal.cmake")) { cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error while executing CPackExt.cmake" << std::endl); + "Error while executing CPackExternal.cmake" << std::endl); return 0; } - std::string major = this->GetOption("CPACK_EXT_SELECTED_MAJOR"); + std::string major = this->GetOption("CPACK_EXTERNAL_SELECTED_MAJOR"); if (major == "1") { - this->Generator = cm::make_unique<cmCPackExtVersion1Generator>(this); + this->Generator = cm::make_unique<cmCPackExternalVersion1Generator>(this); } return this->Superclass::InitializeInternal(); } -int cmCPackExtGenerator::PackageFiles() +int cmCPackExternalGenerator::PackageFiles() { Json::StreamWriterBuilder builder; builder["indentation"] = " "; @@ -57,12 +57,12 @@ int cmCPackExtGenerator::PackageFiles() return 0; } - const char* packageScript = this->GetOption("CPACK_EXT_PACKAGE_SCRIPT"); + const char* packageScript = this->GetOption("CPACK_EXTERNAL_PACKAGE_SCRIPT"); if (packageScript && *packageScript) { if (!cmSystemTools::FileIsFullPath(packageScript)) { cmCPackLogger( cmCPackLog::LOG_ERROR, - "CPACK_EXT_PACKAGE_SCRIPT does not contain a full file path" + "CPACK_EXTERNAL_PACKAGE_SCRIPT does not contain a full file path" << std::endl); return 0; } @@ -77,12 +77,12 @@ int cmCPackExtGenerator::PackageFiles() return 1; } -bool cmCPackExtGenerator::SupportsComponentInstallation() const +bool cmCPackExternalGenerator::SupportsComponentInstallation() const { return true; } -int cmCPackExtGenerator::InstallProjectViaInstallCommands( +int cmCPackExternalGenerator::InstallProjectViaInstallCommands( bool setDestDir, const std::string& tempInstallDirectory) { if (this->StagingEnabled()) { @@ -93,7 +93,7 @@ int cmCPackExtGenerator::InstallProjectViaInstallCommands( return 1; } -int cmCPackExtGenerator::InstallProjectViaInstallScript( +int cmCPackExternalGenerator::InstallProjectViaInstallScript( bool setDestDir, const std::string& tempInstallDirectory) { if (this->StagingEnabled()) { @@ -104,7 +104,7 @@ int cmCPackExtGenerator::InstallProjectViaInstallScript( return 1; } -int cmCPackExtGenerator::InstallProjectViaInstalledDirectories( +int cmCPackExternalGenerator::InstallProjectViaInstalledDirectories( bool setDestDir, const std::string& tempInstallDirectory, const mode_t* default_dir_mode) { @@ -116,7 +116,7 @@ int cmCPackExtGenerator::InstallProjectViaInstalledDirectories( return 1; } -int cmCPackExtGenerator::RunPreinstallTarget( +int cmCPackExternalGenerator::RunPreinstallTarget( const std::string& installProjectName, const std::string& installDirectory, cmGlobalGenerator* globalGenerator, const std::string& buildConfig) { @@ -128,7 +128,7 @@ int cmCPackExtGenerator::RunPreinstallTarget( return 1; } -int cmCPackExtGenerator::InstallCMakeProject( +int cmCPackExternalGenerator::InstallCMakeProject( bool setDestDir, const std::string& installDirectory, const std::string& baseTempInstallDirectory, const mode_t* default_dir_mode, const std::string& component, bool componentInstall, @@ -145,18 +145,19 @@ int cmCPackExtGenerator::InstallCMakeProject( return 1; } -bool cmCPackExtGenerator::StagingEnabled() const +bool cmCPackExternalGenerator::StagingEnabled() const { - return !cmSystemTools::IsOff(this->GetOption("CPACK_EXT_ENABLE_STAGING")); + return !cmSystemTools::IsOff( + this->GetOption("CPACK_EXTERNAL_ENABLE_STAGING")); } -cmCPackExtGenerator::cmCPackExtVersionGenerator::cmCPackExtVersionGenerator( - cmCPackExtGenerator* parent) +cmCPackExternalGenerator::cmCPackExternalVersionGenerator:: + cmCPackExternalVersionGenerator(cmCPackExternalGenerator* parent) : Parent(parent) { } -int cmCPackExtGenerator::cmCPackExtVersionGenerator::WriteVersion( +int cmCPackExternalGenerator::cmCPackExternalVersionGenerator::WriteVersion( Json::Value& root) { root["formatVersionMajor"] = this->GetVersionMajor(); @@ -165,7 +166,7 @@ int cmCPackExtGenerator::cmCPackExtVersionGenerator::WriteVersion( return 1; } -int cmCPackExtGenerator::cmCPackExtVersionGenerator::WriteToJSON( +int cmCPackExternalGenerator::cmCPackExternalVersionGenerator::WriteToJSON( Json::Value& root) { if (!this->WriteVersion(root)) { diff --git a/Source/CPack/cmCPackExtGenerator.h b/Source/CPack/cmCPackExternalGenerator.h index 103e56d..176d6a9 100644 --- a/Source/CPack/cmCPackExtGenerator.h +++ b/Source/CPack/cmCPackExternalGenerator.h @@ -1,7 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackExtGenerator_h -#define cmCPackExtGenerator_h +#ifndef cmCPackExternalGenerator_h +#define cmCPackExternalGenerator_h #include "cmCPackGenerator.h" #include "cm_sys_stat.h" @@ -14,13 +14,13 @@ namespace Json { class Value; } -/** \class cmCPackExtGenerator +/** \class cmCPackExternalGenerator * \brief A generator for CPack External packaging tools */ -class cmCPackExtGenerator : public cmCPackGenerator +class cmCPackExternalGenerator : public cmCPackGenerator { public: - cmCPackTypeMacro(cmCPackExtGenerator, cmCPackGenerator); + cmCPackTypeMacro(cmCPackExternalGenerator, cmCPackGenerator); const char* GetOutputExtension() override { return ".json"; } @@ -54,12 +54,12 @@ protected: private: bool StagingEnabled() const; - class cmCPackExtVersionGenerator + class cmCPackExternalVersionGenerator { public: - cmCPackExtVersionGenerator(cmCPackExtGenerator* parent); + cmCPackExternalVersionGenerator(cmCPackExternalGenerator* parent); - virtual ~cmCPackExtVersionGenerator() = default; + virtual ~cmCPackExternalVersionGenerator() = default; virtual int WriteToJSON(Json::Value& root); @@ -69,20 +69,21 @@ private: int WriteVersion(Json::Value& root); - cmCPackExtGenerator* Parent; + cmCPackExternalGenerator* Parent; }; - class cmCPackExtVersion1Generator : public cmCPackExtVersionGenerator + class cmCPackExternalVersion1Generator + : public cmCPackExternalVersionGenerator { public: - using cmCPackExtVersionGenerator::cmCPackExtVersionGenerator; + using cmCPackExternalVersionGenerator::cmCPackExternalVersionGenerator; protected: int GetVersionMajor() override { return 1; } int GetVersionMinor() override { return 0; } }; - std::unique_ptr<cmCPackExtVersionGenerator> Generator; + std::unique_ptr<cmCPackExternalVersionGenerator> Generator; }; #endif diff --git a/Source/CPack/cmCPackGeneratorFactory.cxx b/Source/CPack/cmCPackGeneratorFactory.cxx index 8ef24f7..2c5ab4d 100644 --- a/Source/CPack/cmCPackGeneratorFactory.cxx +++ b/Source/CPack/cmCPackGeneratorFactory.cxx @@ -12,7 +12,7 @@ # include "cmCPackFreeBSDGenerator.h" #endif #include "cmCPackDebGenerator.h" -#include "cmCPackExtGenerator.h" +#include "cmCPackExternalGenerator.h" #include "cmCPackGenerator.h" #include "cmCPackLog.h" #include "cmCPackNSISGenerator.h" @@ -111,9 +111,9 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory() this->RegisterGenerator("NuGet", "NuGet packages", cmCPackNuGetGenerator::CreateGenerator); } - if (cmCPackExtGenerator::CanGenerate()) { - this->RegisterGenerator("Ext", "CPack External packages", - cmCPackExtGenerator::CreateGenerator); + if (cmCPackExternalGenerator::CanGenerate()) { + this->RegisterGenerator("External", "CPack External packages", + cmCPackExternalGenerator::CreateGenerator); } #ifdef __APPLE__ if (cmCPackDragNDropGenerator::CanGenerate()) { diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index 91ccdf7..53f5593 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -318,12 +318,6 @@ bool cmAddCustomCommandCommand::InitialPass( return false; } - // Convert working directory to a full path. - if (!working.empty()) { - const std::string& build_dir = this->Makefile->GetCurrentBinaryDirectory(); - working = cmSystemTools::CollapseFullPath(working, build_dir); - } - // Choose which mode of the command to use. bool escapeOldStyle = !verbatim; if (source.empty() && output.empty()) { diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx index 82ee6b4..ddd9b70 100644 --- a/Source/cmAddCustomTargetCommand.cxx +++ b/Source/cmAddCustomTargetCommand.cxx @@ -181,13 +181,6 @@ bool cmAddCustomTargetCommand::InitialPass( } } - // Convert working directory to a full path. - if (!working_directory.empty()) { - const std::string& build_dir = this->Makefile->GetCurrentBinaryDirectory(); - working_directory = - cmSystemTools::CollapseFullPath(working_directory, build_dir); - } - if (commandLines.empty() && !byproducts.empty()) { this->Makefile->IssueMessage( cmake::FATAL_ERROR, diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx index 5bbae17..d1c1736 100644 --- a/Source/cmCustomCommandGenerator.cxx +++ b/Source/cmCustomCommandGenerator.cxx @@ -70,6 +70,12 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, std::unique_ptr<cmCompiledGeneratorExpression> cge = this->GE->Parse(workingdirectory); this->WorkingDirectory = cge->Evaluate(this->LG, this->Config); + // Convert working directory to a full path. + if (!this->WorkingDirectory.empty()) { + std::string const& build_dir = this->LG->GetCurrentBinaryDirectory(); + this->WorkingDirectory = + cmSystemTools::CollapseFullPath(this->WorkingDirectory, build_dir); + } } } diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 4cf9dd7..bddc3c4 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -855,7 +855,7 @@ void cmExportFileGenerator::SetImportDetailProperties( std::string propval; if (auto* p = target->GetProperty("COMMON_LANGUAGE_RUNTIME")) { propval = p; - } else if (target->HasLanguage("CSharp", config)) { + } else if (target->IsCSharpOnly()) { // C# projects do not have the /clr flag, so we set the property // here to mark the target as (only) managed (i.e. no .lib file // to link to). Otherwise the COMMON_LANGUAGE_RUNTIME target diff --git a/Source/cmFileMonitor.cxx b/Source/cmFileMonitor.cxx index 04a3c0e..b36ac78 100644 --- a/Source/cmFileMonitor.cxx +++ b/Source/cmFileMonitor.cxx @@ -315,6 +315,7 @@ void cmFileMonitor::MonitorPaths(const std::vector<std::string>& paths, for (std::string const& p : paths) { std::vector<std::string> pathSegments; cmsys::SystemTools::SplitPath(p, pathSegments, true); + const bool pathIsFile = !cmsys::SystemTools::FileIsDirectory(p); const size_t segmentCount = pathSegments.size(); if (segmentCount < 2) { // Expect at least rootdir and filename @@ -324,7 +325,7 @@ void cmFileMonitor::MonitorPaths(const std::vector<std::string>& paths, for (size_t i = 0; i < segmentCount; ++i) { assert(currentWatcher); - const bool fileSegment = (i == segmentCount - 1); + const bool fileSegment = (i == segmentCount - 1 && pathIsFile); const bool rootSegment = (i == 0); assert( !(fileSegment && diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index b8c9598..71947ea 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -5716,20 +5716,23 @@ void cmGeneratorTarget::GetLanguages(std::set<std::string>& languages, } } -bool cmGeneratorTarget::HasLanguage(std::string const& language, - std::string const& config, - bool exclusive) const +bool cmGeneratorTarget::IsCSharpOnly() const { - std::set<std::string> languages; - this->GetLanguages(languages, config); - // The "exclusive" check applies only to source files and not - // the linker language which may be affected by dependencies. - if (exclusive && languages.size() > 1) { + // Only certain target types may compile CSharp. + if (this->GetType() != cmStateEnums::SHARED_LIBRARY && + this->GetType() != cmStateEnums::STATIC_LIBRARY && + this->GetType() != cmStateEnums::EXECUTABLE) { return false; } - // add linker language (if it is different from compiler languages) - languages.insert(this->GetLinkerLanguage(config)); - return languages.count(language) > 0; + std::set<std::string> languages; + this->GetLanguages(languages, ""); + // Consider an explicit linker language property, but *not* the + // computed linker language that may depend on linked targets. + const char* linkLang = this->GetProperty("LINKER_LANGUAGE"); + if (linkLang && *linkLang) { + languages.insert(linkLang); + } + return languages.size() == 1 && languages.count("CSharp") > 0; } void cmGeneratorTarget::ComputeLinkImplementationLanguages( @@ -6095,6 +6098,5 @@ cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType( // C# targets are always managed. This language specific check // is added to avoid that the COMMON_LANGUAGE_RUNTIME target property // has to be set manually for C# targets. - return this->HasLanguage("CSharp", config) ? ManagedType::Managed - : ManagedType::Native; + return this->IsCSharpOnly() ? ManagedType::Managed : ManagedType::Native; } diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 4c32558..5e7cf12 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -372,11 +372,7 @@ public: void GetLanguages(std::set<std::string>& languages, std::string const& config) const; - // Evaluate if the target uses the given language for compilation - // and/or linking. If 'exclusive' is true, 'language' is expected - // to be the only language used in source files for the target. - bool HasLanguage(std::string const& language, std::string const& config, - bool exclusive = true) const; + bool IsCSharpOnly() const; void GetObjectLibrariesCMP0026( std::vector<cmGeneratorTarget*>& objlibs) const; diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index a216346..3be09b0 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -98,7 +98,7 @@ void cmGlobalVisualStudio71Generator::WriteProject(std::ostream& fout, ext = ".vfproj"; project = "Project(\"{6989167D-11E4-40FE-8C1A-2192A86A7E90}\") = \""; } - if (t->HasLanguage("CSharp", "")) { + if (t->IsCSharpOnly()) { ext = ".csproj"; project = "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \""; } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index d5a18e6..a065408 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -65,6 +65,7 @@ static const char* ruleReplaceVars[] = { "CMAKE_CURRENT_BINARY_DIR", "CMAKE_RANLIB", "CMAKE_LINKER", + "CMAKE_MT", "CMAKE_CUDA_HOST_COMPILER", "CMAKE_CUDA_HOST_LINK_LAUNCHER", "CMAKE_CL_SHOWINCLUDES_PREFIX" diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index eb31478..66edc91 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -17,6 +17,7 @@ #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmGlobalNinjaGenerator.h" +#include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmNinjaTargetGenerator.h" #include "cmRulePlaceholderExpander.h" @@ -40,8 +41,7 @@ cmRulePlaceholderExpander* cmLocalNinjaGenerator::CreateRulePlaceholderExpander() const { cmRulePlaceholderExpander* ret = - new cmRulePlaceholderExpander(this->Compilers, this->VariableMappings, - this->CompilerSysroot, this->LinkerSysroot); + this->cmLocalGenerator::CreateRulePlaceholderExpander(); ret->SetTargetImpLib("$TARGET_IMPLIB"); return ret; } diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 6436969..1386706 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -506,7 +506,6 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd() gt.GetFullPath(cfgName, cmStateEnums::RuntimeBinaryArtifact, /*realname=*/true)); cmakeCommand += targetOutputReal; - cmakeCommand += " || true"; linkCmds.push_back(std::move(cmakeCommand)); } return linkCmds; diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 7700767..a213c84 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -322,6 +322,9 @@ bool cmQtAutoGenInitializer::InitCustomTargets() // Autogen target: Compute user defined dependencies { + this->AutogenTarget.DependOrigin = + this->Target->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS"); + std::string const deps = this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS"); if (!deps.empty()) { @@ -904,7 +907,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() // Add link library target dependencies to the autogen target // dependencies - { + if (this->AutogenTarget.DependOrigin) { // add_dependencies/addUtility do not support generator expressions. // We depend only on the libraries found in all configs therefore. std::map<cmGeneratorTarget const*, std::size_t> commonTargets; @@ -941,8 +944,10 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() new cmGeneratorTarget(autogenTarget, localGen)); // Forward origin utilities to autogen target - for (BT<std::string> const& depName : this->Target->GetUtilities()) { - autogenTarget->AddUtility(depName.Value, makefile); + if (this->AutogenTarget.DependOrigin) { + for (BT<std::string> const& depName : this->Target->GetUtilities()) { + autogenTarget->AddUtility(depName.Value, makefile); + } } // Add additional autogen target dependencies to autogen target for (cmTarget* depTarget : this->AutogenTarget.DependTargets) { diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index ce00e00..1d3947b 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -107,6 +107,7 @@ private: std::string SettingsFile; std::map<std::string, std::string> ConfigSettingsFile; // Dependencies + bool DependOrigin = false; std::set<std::string> DependFiles; std::set<cmTarget*> DependTargets; // Sources to process diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 987bdb3..5d76a02 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -239,6 +239,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->SetPropertyDefault("AUTOMOC", nullptr); this->SetPropertyDefault("AUTOUIC", nullptr); this->SetPropertyDefault("AUTORCC", nullptr); + this->SetPropertyDefault("AUTOGEN_ORIGIN_DEPENDS", nullptr); this->SetPropertyDefault("AUTOGEN_PARALLEL", nullptr); this->SetPropertyDefault("AUTOMOC_COMPILER_PREDEFINES", nullptr); this->SetPropertyDefault("AUTOMOC_DEPEND_FILTERS", nullptr); diff --git a/Source/cmTargetPropCommandBase.cxx b/Source/cmTargetPropCommandBase.cxx index 9a8fd96..1b8ee81 100644 --- a/Source/cmTargetPropCommandBase.cxx +++ b/Source/cmTargetPropCommandBase.cxx @@ -84,15 +84,6 @@ bool cmTargetPropCommandBase::ProcessContentArgs( this->SetError("called with invalid arguments"); return false; } - if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY && - scope != "INTERFACE") { - this->SetError("may only set INTERFACE properties on INTERFACE targets"); - return false; - } - if (this->Target->IsImported() && scope != "INTERFACE") { - this->SetError("may only set INTERFACE properties on IMPORTED targets"); - return false; - } ++argIndex; @@ -101,10 +92,21 @@ bool cmTargetPropCommandBase::ProcessContentArgs( for (unsigned int i = argIndex; i < args.size(); ++i, ++argIndex) { if (args[i] == "PUBLIC" || args[i] == "PRIVATE" || args[i] == "INTERFACE") { - return this->PopulateTargetProperies(scope, content, prepend, system); + break; } content.push_back(args[i]); } + if (!content.empty()) { + if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY && + scope != "INTERFACE") { + this->SetError("may only set INTERFACE properties on INTERFACE targets"); + return false; + } + if (this->Target->IsImported() && scope != "INTERFACE") { + this->SetError("may only set INTERFACE properties on IMPORTED targets"); + return false; + } + } return this->PopulateTargetProperies(scope, content, prepend, system); } @@ -112,6 +114,9 @@ bool cmTargetPropCommandBase::PopulateTargetProperies( const std::string& scope, const std::vector<std::string>& content, bool prepend, bool system) { + if (content.empty()) { + return true; + } if (scope == "PRIVATE" || scope == "PUBLIC") { if (!this->HandleDirectContent(this->Target, content, prepend, system)) { return false; diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index eb1a27e..e9a1a67 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -205,12 +205,11 @@ static bool cmVS10IsTargetsFile(std::string const& path) return cmSystemTools::Strucmp(ext.c_str(), ".targets") == 0; } -static std::string computeProjectFileExtension(cmGeneratorTarget const* t, - const std::string& config) +static std::string computeProjectFileExtension(cmGeneratorTarget const* t) { std::string res; res = ".vcxproj"; - if (t->HasLanguage("CSharp", config)) { + if (t->IsCSharpOnly()) { res = ".csproj"; } return res; @@ -305,8 +304,8 @@ void cmVisualStudio10TargetGenerator::Generate() this->GeneratorTarget->GetProperty("EXTERNAL_MSPROJECT")) { return; } - const std::string ProjectFileExtension = computeProjectFileExtension( - this->GeneratorTarget, *this->Configurations.begin()); + const std::string ProjectFileExtension = + computeProjectFileExtension(this->GeneratorTarget); if (ProjectFileExtension == ".vcxproj") { this->ProjectType = vcxproj; this->Managed = false; @@ -1398,8 +1397,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups() std::string path = this->LocalGenerator->GetCurrentBinaryDirectory(); path += "/"; path += this->Name; - path += computeProjectFileExtension(this->GeneratorTarget, - *this->Configurations.begin()); + path += computeProjectFileExtension(this->GeneratorTarget); path += ".filters"; cmGeneratedFileStream fout(path); fout.SetCopyIfDifferent(true); @@ -3800,7 +3798,7 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) path = lg->GetCurrentBinaryDirectory(); path += "/"; path += dt->GetName(); - path += computeProjectFileExtension(dt, *this->Configurations.begin()); + path += computeProjectFileExtension(dt); } ConvertToWindowsSlash(path); Elem e2(e1, "ProjectReference"); @@ -3826,7 +3824,7 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) } // Workaround for static library C# targets if (referenceNotManaged && dt->GetType() == cmStateEnums::STATIC_LIBRARY) { - referenceNotManaged = !dt->HasLanguage("CSharp", ""); + referenceNotManaged = !dt->IsCSharpOnly(); } if (referenceNotManaged) { e2.Element("ReferenceOutputAssembly", "false"); diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index e2225ff..8a140e8 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -1521,6 +1521,8 @@ class cmVSLink std::string ManifestFileRC; std::string ManifestFileRes; std::string TargetFile; + std::string MtPath; + std::string RcPath; public: cmVSLink(int type, bool verbose) @@ -1664,6 +1666,12 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, } else if (cmHasLiteralPrefix(*arg, "--intdir=")) { intDir = arg->substr(9); ++arg; + } else if (cmHasLiteralPrefix(*arg, "--rc=")) { + this->RcPath = arg->substr(5); + ++arg; + } else if (cmHasLiteralPrefix(*arg, "--mt=")) { + this->MtPath = arg->substr(5); + ++arg; } else { std::cerr << "unknown argument '" << *arg << "'\n"; return false; @@ -1803,7 +1811,7 @@ int cmVSLink::LinkIncremental() // Compile the resource file. std::vector<std::string> rcCommand; - rcCommand.push_back("rc"); + rcCommand.push_back(this->RcPath.empty() ? "rc" : this->RcPath); rcCommand.push_back("/fo" + this->ManifestFileRes); rcCommand.push_back(this->ManifestFileRC); if (!RunCommand("RC Pass 1", rcCommand, this->Verbose, FORMAT_DECIMAL)) { @@ -1862,7 +1870,7 @@ int cmVSLink::LinkNonIncremental() int cmVSLink::RunMT(std::string const& out, bool notify) { std::vector<std::string> mtCommand; - mtCommand.push_back("mt"); + mtCommand.push_back(this->MtPath.empty() ? "mt" : this->MtPath); mtCommand.push_back("/nologo"); mtCommand.push_back("/manifest"); if (this->LinkGeneratesManifest) { diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index f74defb..fe27796 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1384,6 +1384,10 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release add_subdirectory(FindFreetype) endif() + if(CMake_TEST_FindGDAL) + add_subdirectory(FindGDAL) + endif() + if(CMake_TEST_FindGSL) add_subdirectory(FindGSL) endif() diff --git a/Tests/CSharpOnly/CMakeLists.txt b/Tests/CSharpOnly/CMakeLists.txt index 84b58ca..82049c7 100644 --- a/Tests/CSharpOnly/CMakeLists.txt +++ b/Tests/CSharpOnly/CMakeLists.txt @@ -9,5 +9,5 @@ add_executable(CSharpOnly csharponly.cs) target_link_libraries(CSharpOnly lib1 lib2) -add_custom_target(CSharpCustom SOURCES empty.cs) -add_custom_target(custom.cs DEPENDS empty.txt) +add_custom_target(CSharpCustom ALL SOURCES empty.cs) +add_custom_target(custom.cs ALL DEPENDS empty.txt) diff --git a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt index 2e12a78..3bab1fe 100644 --- a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt +++ b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt @@ -47,7 +47,7 @@ file(MAKE_DIRECTORY ${TestWorkingDir_BINARY_DIR}/genex) add_custom_command( OUTPUT "${TestWorkingDir_BINARY_DIR}/genex/working.c" COMMAND "${CMAKE_COMMAND}" -E copy "${TestWorkingDir_SOURCE_DIR}/working.c.in" "${TestWorkingDir_BINARY_DIR}/genex/working.c" - WORKING_DIRECTORY "${TestWorkingDir_BINARY_DIR}/$<1:genex>/" + WORKING_DIRECTORY "$<0:not_used/>${TestWorkingDir_BINARY_DIR}/$<1:genex>/" COMMENT "custom command" ) @@ -58,7 +58,7 @@ add_custom_target( CustomGenex ALL COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${TestWorkingDir_SOURCE_DIR}/customTarget.c" "${TestWorkingDir_BINARY_DIR}/genex/customTarget.c" BYPRODUCTS "${TestWorkingDir_BINARY_DIR}/genex/customTarget.c" - WORKING_DIRECTORY "${TestWorkingDir_BINARY_DIR}/$<1:genex>/" + WORKING_DIRECTORY "$<0:not_used/>${TestWorkingDir_BINARY_DIR}/$<1:genex>/" ) add_dependencies(workinggenex CustomGenex) diff --git a/Tests/FindGDAL/CMakeLists.txt b/Tests/FindGDAL/CMakeLists.txt new file mode 100644 index 0000000..12f95e1 --- /dev/null +++ b/Tests/FindGDAL/CMakeLists.txt @@ -0,0 +1,10 @@ +add_test(NAME FindGDAL.Test COMMAND + ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION> + --build-and-test + "${CMake_SOURCE_DIR}/Tests/FindGDAL/Test" + "${CMake_BINARY_DIR}/Tests/FindGDAL/Test" + ${build_generator_args} + --build-project TestFindGDAL + --build-options ${build_options} + --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION> + ) diff --git a/Tests/FindGDAL/Test/CMakeLists.txt b/Tests/FindGDAL/Test/CMakeLists.txt new file mode 100644 index 0000000..8bdc57c --- /dev/null +++ b/Tests/FindGDAL/Test/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.10) +project(TestFindGDAL C) +include(CTest) + +find_package(GDAL REQUIRED) + +add_definitions(-DCMAKE_EXPECTED_GDAL_VERSION="${GDAL_VERSION}") + +add_executable(test_tgt main.c) +target_link_libraries(test_tgt GDAL::GDAL) +add_test(NAME test_tgt COMMAND test_tgt) + +add_executable(test_var main.c) +target_include_directories(test_var PRIVATE ${GDAL_INCLUDE_DIRS}) +target_link_libraries(test_var PRIVATE ${GDAL_LIBRARIES}) +add_test(NAME test_var COMMAND test_var) diff --git a/Tests/FindGDAL/Test/main.c b/Tests/FindGDAL/Test/main.c new file mode 100644 index 0000000..7b31a13 --- /dev/null +++ b/Tests/FindGDAL/Test/main.c @@ -0,0 +1,11 @@ +#include <gdal.h> +#include <stdio.h> +#include <string.h> + +int main() +{ + printf("Found GDAL version %s, expected version %s\n", GDAL_RELEASE_NAME, + CMAKE_EXPECTED_GDAL_VERSION); + GDALAllRegister(); + return strcmp(GDAL_RELEASE_NAME, CMAKE_EXPECTED_GDAL_VERSION); +} diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/CMakeLists.txt b/Tests/QtAutogen/AutogenOriginDependsOff/CMakeLists.txt new file mode 100644 index 0000000..1c2271a --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 3.11) +project(AutogenOriginDependsOff) +include("../AutogenTest.cmake") + +set(CSD ${CMAKE_CURRENT_SOURCE_DIR}) +set(CBD ${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CSD}) +include_directories(${CBD}) + +# A GENERATED file ensures there will be an _autogen target in VS +add_custom_command ( + OUTPUT "${CBD}/config.hpp" + COMMAND ${CMAKE_COMMAND} -E copy "${CSD}/config.hpp.in" "${CBD}/config.hpp" + ) + + +# Library "a_mc" provides a header that holds a string with the content of +# mocs_compilation.cpp from a_qt. It therefore must depend on a_qt_autogen. +add_custom_target ( a_mc + COMMAND ${CMAKE_COMMAND} -E sleep 2 + COMMAND ${CMAKE_COMMAND} + "-DMCF=${CBD}/a_qt_autogen/mocs_compilation.cpp" + "-DCF_IN=${CSD}/a_mc.hpp.in" + "-DCF_OUT=${CBD}/a_mc.hpp" + -P ${CSD}/configure_content.cmake + ) +add_dependencies ( a_mc a_qt_autogen ) + +# Library "a_qt" +# - depends on a GENERATED file +# - AUTOMOC enabled +# - depends on a target (a_mc) that depends on a_qt_qutogen +add_library ( a_qt a_qt.cpp "${CBD}/config.hpp" ) +add_dependencies ( a_qt a_mc ) +target_link_libraries ( a_qt ${QT_QTCORE_TARGET}) +set_target_properties ( a_qt PROPERTIES AUTOMOC TRUE) +# Disable AUTOGEN_ORIGIN_DEPENDS to avoid loop dependencies +set_target_properties ( a_qt PROPERTIES AUTOGEN_ORIGIN_DEPENDS OFF) + + +# Library "b_mc" provides a header that holds a string function that returns +# the content of mocs_compilation.cpp from b_qt. +# It therefore must depend on b_qt_autogen. +add_custom_command ( + OUTPUT ${CBD}/b_mc.cpp + DEPENDS b_qt_autogen + COMMAND ${CMAKE_COMMAND} -E sleep 2 + COMMAND ${CMAKE_COMMAND} + "-DMCF=${CBD}/b_qt_autogen/mocs_compilation.cpp" + "-DCF_IN=${CSD}/b_mc.cpp.in" + "-DCF_OUT=${CBD}/b_mc.cpp" + -P ${CSD}/configure_content.cmake + ) +add_library ( b_mc ${CSD}/b_mc.hpp ${CBD}/b_mc.cpp ) + +# Library "b_qt" +# - depends on a GENERATED file +# - AUTOMOC enabled +# - depends on a library (b_mc) that depends on b_qt_qutogen +add_library ( b_qt b_qt.cpp "${CBD}/config.hpp" ) +target_link_libraries ( b_qt b_mc ) +target_link_libraries ( b_qt ${QT_QTCORE_TARGET}) +set_target_properties ( b_qt PROPERTIES AUTOMOC TRUE) +# Disable AUTOGEN_ORIGIN_DEPENDS to avoid loop dependencies +set_target_properties ( b_qt PROPERTIES AUTOGEN_ORIGIN_DEPENDS OFF) + + +# The main target depends on both libraries which depend on the _autogen +# target of the main target. +add_executable ( autogenOriginDependsOff main.cpp ) +target_link_libraries ( autogenOriginDependsOff a_qt b_qt ) diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/a_mc.hpp.in b/Tests/QtAutogen/AutogenOriginDependsOff/a_mc.hpp.in new file mode 100644 index 0000000..fe71f67 --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/a_mc.hpp.in @@ -0,0 +1,9 @@ +#ifndef A_MC_HPP +#define A_MC_HPP + +namespace a_mc { + +char const* mocs_compilation = "@MOCS_COMPILATION@"; +} + +#endif diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.cpp b/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.cpp new file mode 100644 index 0000000..e498969 --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.cpp @@ -0,0 +1,28 @@ + +#include "a_qt.hpp" +#include <a_mc.hpp> + +namespace a_qt { + +/// @brief A source local QObject based class +class Source_QObject : public QObject +{ + Q_OBJECT +public: + Source_QObject() {} + ~Source_QObject() {} + + std::string str; +}; + +std::string mocs_compilation() +{ + // Create and destroy QObject based classes + Header_QObject header_obj; + Source_QObject source_obj; + + return std::string(a_mc::mocs_compilation); +} +} + +#include "a_qt.moc" diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.hpp b/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.hpp new file mode 100644 index 0000000..e2387ee --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.hpp @@ -0,0 +1,25 @@ +#ifndef A_QT_HPP +#define A_QT_HPP + +#include <QObject> +#include <config.hpp> +#include <string> + +namespace a_qt { + +/// @brief A header local QObject based class +class Header_QObject : public QObject +{ + Q_OBJECT +public: + Header_QObject() {} + ~Header_QObject() {} + + std::string str; +}; + +/// @brief Function that returns the content of mocs_compilation.cpp +extern std::string mocs_compilation(); +} + +#endif diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/b_mc.cpp.in b/Tests/QtAutogen/AutogenOriginDependsOff/b_mc.cpp.in new file mode 100644 index 0000000..0f5ec30 --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/b_mc.cpp.in @@ -0,0 +1,9 @@ +#include <b_mc.hpp> + +namespace b_mc { + +char const* mocs_compilation() +{ + return "@MOCS_COMPILATION@"; +} +} diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/b_mc.hpp b/Tests/QtAutogen/AutogenOriginDependsOff/b_mc.hpp new file mode 100644 index 0000000..0437273 --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/b_mc.hpp @@ -0,0 +1,9 @@ +#ifndef B_MC_HPP +#define B_MC_HPP + +namespace b_mc { + +extern char const* mocs_compilation(); +} + +#endif diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.cpp b/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.cpp new file mode 100644 index 0000000..f72f6ca --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.cpp @@ -0,0 +1,28 @@ + +#include "b_qt.hpp" +#include <b_mc.hpp> + +namespace b_qt { + +/// @brief A source local QObject based class +class Source_QObject : public QObject +{ + Q_OBJECT +public: + Source_QObject() {} + ~Source_QObject() {} + + std::string str; +}; + +std::string mocs_compilation() +{ + // Create and destroy QObject based classes + Header_QObject header_obj; + Source_QObject source_obj; + + return std::string(b_mc::mocs_compilation()); +} +} + +#include "b_qt.moc" diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.hpp b/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.hpp new file mode 100644 index 0000000..d7f0311 --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.hpp @@ -0,0 +1,25 @@ +#ifndef B_QT_HPP +#define B_QT_HPP + +#include <QObject> +#include <config.hpp> +#include <string> + +namespace b_qt { + +/// @brief A header local QObject based class +class Header_QObject : public QObject +{ + Q_OBJECT +public: + Header_QObject() {} + ~Header_QObject() {} + + std::string str; +}; + +/// @brief Function that returns the content of mocs_compilation.cpp +extern std::string mocs_compilation(); +} + +#endif diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/config.hpp.in b/Tests/QtAutogen/AutogenOriginDependsOff/config.hpp.in new file mode 100644 index 0000000..e415d08 --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/config.hpp.in @@ -0,0 +1,8 @@ +#ifndef CONFIG_HPP +#define CONFIG_HPP + +// Application configuration + +enum dummy { NO_OP }; + +#endif diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/configure_content.cmake b/Tests/QtAutogen/AutogenOriginDependsOff/configure_content.cmake new file mode 100644 index 0000000..0fc6e63 --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/configure_content.cmake @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) + +# Read mocs_compilation.cpp file into variable +file(READ "${MCF}" MOCS_COMPILATION) +string(REPLACE "\\" "\\\\" MOCS_COMPILATION "${MOCS_COMPILATION}" ) +string(REPLACE "\"" "\\\"" MOCS_COMPILATION "${MOCS_COMPILATION}" ) +string(REPLACE "\n" "\"\n\"" MOCS_COMPILATION "${MOCS_COMPILATION}" ) + +# Configure file +configure_file ( "${CF_IN}" "${CF_OUT}" @ONLY ) diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/main.cpp b/Tests/QtAutogen/AutogenOriginDependsOff/main.cpp new file mode 100644 index 0000000..a3425f1 --- /dev/null +++ b/Tests/QtAutogen/AutogenOriginDependsOff/main.cpp @@ -0,0 +1,15 @@ + +#include <a_qt.hpp> +#include <b_qt.hpp> +#include <string> + +int main() +{ + if (a_qt::mocs_compilation().empty()) { + return -1; + } + if (b_qt::mocs_compilation().empty()) { + return -1; + } + return 0; +} diff --git a/Tests/QtAutogen/MocDepends/CMakeLists.txt b/Tests/QtAutogen/AutogenOriginDependsOn/CMakeLists.txt index 6ea72be..60869eb 100644 --- a/Tests/QtAutogen/MocDepends/CMakeLists.txt +++ b/Tests/QtAutogen/AutogenOriginDependsOn/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(MocDepends) +project(AutogenOriginDependsOn) include("../AutogenTest.cmake") include_directories(${CMAKE_CURRENT_BINARY_DIR}) @@ -89,51 +89,3 @@ target_link_libraries(SimpleLib ${QT_QTCORE_TARGET}) add_executable(mocDepGenLib testGenLib.cpp) target_link_libraries(mocDepGenLib SimpleLib ${QT_QTCORE_TARGET}) set_target_properties(mocDepGenLib PROPERTIES AUTOMOC TRUE) - - -# -- Test AUTOGEN_TARGET_DEPENDS with GENERATED file dependency -# -# This tests the dependency of the mocDepATDFile_autogen target of -# mocDepATDTarget to the utility target mocDepATDFileUtil. -# If mocDepATDFile_autogen gets built *before* or in *parallel* to -# mocDepATDFileUtil, the build will fail. That's -# because ATDFile.hpp, which is required by mocDepATDFile_autogen, -# is only valid after the mocDepATDFileUtil build has been completed. -# -# The sleep seconds artificially increase the build time of -# mocDepATDFileUtil to simulate a slow utility target build that takes -# longer to run than the build of the mocDepATDFile_autogen target. -add_custom_command( - OUTPUT ${CBD}/ATDFile.hpp - COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/object_invalid.hpp.in ${CBD}/ATDFile.hpp - COMMAND ${CMAKE_COMMAND} -E sleep 3 - COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/object_valid.hpp.in ${CBD}/ATDFile.hpp) - -add_executable(mocDepATDFile testATDFile.cpp) -target_link_libraries(mocDepATDFile ${QT_QTCORE_TARGET}) -set_target_properties(mocDepATDFile PROPERTIES AUTOMOC TRUE) -set_target_properties(mocDepATDFile PROPERTIES AUTOGEN_TARGET_DEPENDS ${CBD}/ATDFile.hpp) - - -# -- Test AUTOGEN_TARGET_DEPENDS with target dependency -# -# This tests the dependency of the mocDepATDTarget_autogen target of -# mocDepATDTarget to the utility target mocDepATDTargetUtil. -# If mocDepATDTarget_autogen gets built *before* or in *parallel* to -# mocDepATDTargetUtil, the build will fail. That's -# because ATDTarget.hpp, which is required by mocDepATDTarget_autogen, -# is only valid after the mocDepATDTargetUtil build has been completed. -# -# The sleep seconds artificially increase the build time of -# mocDepATDTargetUtil to simulate a slow utility target build that takes -# longer to run than the build of the mocDepATDTarget_autogen target. -add_custom_target(mocDepATDTargetUtil - BYPRODUCTS ${CBD}/ATDTarget.hpp - COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/object_invalid.hpp.in ${CBD}/ATDTarget.hpp - COMMAND ${CMAKE_COMMAND} -E sleep 3 - COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/object_valid.hpp.in ${CBD}/ATDTarget.hpp) - -add_executable(mocDepATDTarget testATDTarget.cpp) -target_link_libraries(mocDepATDTarget ${QT_QTCORE_TARGET}) -set_target_properties(mocDepATDTarget PROPERTIES AUTOMOC TRUE) -set_target_properties(mocDepATDTarget PROPERTIES AUTOGEN_TARGET_DEPENDS mocDepATDTargetUtil) diff --git a/Tests/QtAutogen/MocDepends/object_invalid.hpp.in b/Tests/QtAutogen/AutogenOriginDependsOn/object_invalid.hpp.in index 854d9a1..854d9a1 100644 --- a/Tests/QtAutogen/MocDepends/object_invalid.hpp.in +++ b/Tests/QtAutogen/AutogenOriginDependsOn/object_invalid.hpp.in diff --git a/Tests/QtAutogen/MocDepends/object_valid.hpp.in b/Tests/QtAutogen/AutogenOriginDependsOn/object_valid.hpp.in index f364f7c..f364f7c 100644 --- a/Tests/QtAutogen/MocDepends/object_valid.hpp.in +++ b/Tests/QtAutogen/AutogenOriginDependsOn/object_valid.hpp.in diff --git a/Tests/QtAutogen/MocDepends/simpleLib.cpp.in b/Tests/QtAutogen/AutogenOriginDependsOn/simpleLib.cpp.in index fa33bd3..fa33bd3 100644 --- a/Tests/QtAutogen/MocDepends/simpleLib.cpp.in +++ b/Tests/QtAutogen/AutogenOriginDependsOn/simpleLib.cpp.in diff --git a/Tests/QtAutogen/MocDepends/simpleLib.hpp.in b/Tests/QtAutogen/AutogenOriginDependsOn/simpleLib.hpp.in index b65b0cb..b65b0cb 100644 --- a/Tests/QtAutogen/MocDepends/simpleLib.hpp.in +++ b/Tests/QtAutogen/AutogenOriginDependsOn/simpleLib.hpp.in diff --git a/Tests/QtAutogen/MocDepends/testGenFile.cpp b/Tests/QtAutogen/AutogenOriginDependsOn/testGenFile.cpp index 7df6e13..7df6e13 100644 --- a/Tests/QtAutogen/MocDepends/testGenFile.cpp +++ b/Tests/QtAutogen/AutogenOriginDependsOn/testGenFile.cpp diff --git a/Tests/QtAutogen/MocDepends/testGenLib.cpp b/Tests/QtAutogen/AutogenOriginDependsOn/testGenLib.cpp index c14e159..c14e159 100644 --- a/Tests/QtAutogen/MocDepends/testGenLib.cpp +++ b/Tests/QtAutogen/AutogenOriginDependsOn/testGenLib.cpp diff --git a/Tests/QtAutogen/MocDepends/testGenLib.hpp b/Tests/QtAutogen/AutogenOriginDependsOn/testGenLib.hpp index 408335b..408335b 100644 --- a/Tests/QtAutogen/MocDepends/testGenLib.hpp +++ b/Tests/QtAutogen/AutogenOriginDependsOn/testGenLib.hpp diff --git a/Tests/QtAutogen/MocDepends/testGenTarget.cpp b/Tests/QtAutogen/AutogenOriginDependsOn/testGenTarget.cpp index 911076e..911076e 100644 --- a/Tests/QtAutogen/MocDepends/testGenTarget.cpp +++ b/Tests/QtAutogen/AutogenOriginDependsOn/testGenTarget.cpp diff --git a/Tests/QtAutogen/AutogenTargetDepends/CMakeLists.txt b/Tests/QtAutogen/AutogenTargetDepends/CMakeLists.txt new file mode 100644 index 0000000..63b7c98 --- /dev/null +++ b/Tests/QtAutogen/AutogenTargetDepends/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.10) +project(AutogenTargetDepends) +include("../AutogenTest.cmake") + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +set(CSD ${CMAKE_CURRENT_SOURCE_DIR}) +set(CBD ${CMAKE_CURRENT_BINARY_DIR}) + +# -- Test AUTOGEN_TARGET_DEPENDS with GENERATED file dependency +# +# This tests the dependency of the mocDepATDFile_autogen target of +# mocDepATDTarget to the utility target mocDepATDFileUtil. +# If mocDepATDFile_autogen gets built *before* or in *parallel* to +# mocDepATDFileUtil, the build will fail. That's +# because ATDFile.hpp, which is required by mocDepATDFile_autogen, +# is only valid after the mocDepATDFileUtil build has been completed. +# +# The sleep seconds artificially increase the build time of +# mocDepATDFileUtil to simulate a slow utility target build that takes +# longer to run than the build of the mocDepATDFile_autogen target. +add_custom_command( + OUTPUT ${CBD}/ATDFile.hpp + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/object_invalid.hpp.in ${CBD}/ATDFile.hpp + COMMAND ${CMAKE_COMMAND} -E sleep 3 + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/object_valid.hpp.in ${CBD}/ATDFile.hpp) + +add_executable(mocDepATDFile testATDFile.cpp) +target_link_libraries(mocDepATDFile ${QT_QTCORE_TARGET}) +set_target_properties(mocDepATDFile PROPERTIES AUTOMOC TRUE) +set_target_properties(mocDepATDFile PROPERTIES AUTOGEN_TARGET_DEPENDS ${CBD}/ATDFile.hpp) + + +# -- Test AUTOGEN_TARGET_DEPENDS with target dependency +# +# This tests the dependency of the mocDepATDTarget_autogen target of +# mocDepATDTarget to the utility target mocDepATDTargetUtil. +# If mocDepATDTarget_autogen gets built *before* or in *parallel* to +# mocDepATDTargetUtil, the build will fail. That's +# because ATDTarget.hpp, which is required by mocDepATDTarget_autogen, +# is only valid after the mocDepATDTargetUtil build has been completed. +# +# The sleep seconds artificially increase the build time of +# mocDepATDTargetUtil to simulate a slow utility target build that takes +# longer to run than the build of the mocDepATDTarget_autogen target. +add_custom_target(mocDepATDTargetUtil + BYPRODUCTS ${CBD}/ATDTarget.hpp + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/object_invalid.hpp.in ${CBD}/ATDTarget.hpp + COMMAND ${CMAKE_COMMAND} -E sleep 3 + COMMAND ${CMAKE_COMMAND} -E copy ${CSD}/object_valid.hpp.in ${CBD}/ATDTarget.hpp) + +add_executable(mocDepATDTarget testATDTarget.cpp) +target_link_libraries(mocDepATDTarget ${QT_QTCORE_TARGET}) +set_target_properties(mocDepATDTarget PROPERTIES AUTOMOC TRUE) +set_target_properties(mocDepATDTarget PROPERTIES AUTOGEN_TARGET_DEPENDS mocDepATDTargetUtil) diff --git a/Tests/QtAutogen/AutogenTargetDepends/object_invalid.hpp.in b/Tests/QtAutogen/AutogenTargetDepends/object_invalid.hpp.in new file mode 100644 index 0000000..854d9a1 --- /dev/null +++ b/Tests/QtAutogen/AutogenTargetDepends/object_invalid.hpp.in @@ -0,0 +1 @@ +#ifndef diff --git a/Tests/QtAutogen/AutogenTargetDepends/object_valid.hpp.in b/Tests/QtAutogen/AutogenTargetDepends/object_valid.hpp.in new file mode 100644 index 0000000..f364f7c --- /dev/null +++ b/Tests/QtAutogen/AutogenTargetDepends/object_valid.hpp.in @@ -0,0 +1,14 @@ +#ifndef OBJECT_HPP +#define OBJECT_HPP + +#include <QObject> + +class Object : public QObject +{ + Q_OBJECT +public: + Q_SLOT + void aSlot(){}; +}; + +#endif diff --git a/Tests/QtAutogen/MocDepends/testATDFile.cpp b/Tests/QtAutogen/AutogenTargetDepends/testATDFile.cpp index 6bddfcd..6bddfcd 100644 --- a/Tests/QtAutogen/MocDepends/testATDFile.cpp +++ b/Tests/QtAutogen/AutogenTargetDepends/testATDFile.cpp diff --git a/Tests/QtAutogen/MocDepends/testATDTarget.cpp b/Tests/QtAutogen/AutogenTargetDepends/testATDTarget.cpp index 831fc26..831fc26 100644 --- a/Tests/QtAutogen/MocDepends/testATDTarget.cpp +++ b/Tests/QtAutogen/AutogenTargetDepends/testATDTarget.cpp diff --git a/Tests/QtAutogen/CommonTests.cmake b/Tests/QtAutogen/CommonTests.cmake index 01ed7e9..58d9f0b 100644 --- a/Tests/QtAutogen/CommonTests.cmake +++ b/Tests/QtAutogen/CommonTests.cmake @@ -15,7 +15,9 @@ ADD_AUTOGEN_TEST(RccSkipSource) if(QT_TEST_VERSION GREATER 4) ADD_AUTOGEN_TEST(MocMacroName mocMacroName) endif() -ADD_AUTOGEN_TEST(MocDepends) +ADD_AUTOGEN_TEST(AutogenOriginDependsOff autogenOriginDependsOff) +ADD_AUTOGEN_TEST(AutogenOriginDependsOn) +ADD_AUTOGEN_TEST(AutogenTargetDepends) if(QT_TEST_ALLOW_QT_MACROS) ADD_AUTOGEN_TEST(MocIncludeStrict mocIncludeStrict) ADD_AUTOGEN_TEST(MocIncludeRelaxed mocIncludeRelaxed) diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 394dd6d..e23926f 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -343,11 +343,16 @@ endif() add_RunCMake_test(File_Generate) add_RunCMake_test(ExportWithoutLanguage) +add_RunCMake_test(target_link_directories) add_RunCMake_test(target_link_libraries) add_RunCMake_test(add_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}) add_RunCMake_test(target_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}) +add_RunCMake_test(target_compile_definitions) add_RunCMake_test(target_compile_features) +add_RunCMake_test(target_compile_options) +add_RunCMake_test(target_include_directories) +add_RunCMake_test(target_sources) add_RunCMake_test(CheckModules) add_RunCMake_test(CheckIPOSupported) add_RunCMake_test(CommandLine -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) @@ -434,7 +439,7 @@ if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") add_RunCMake_test(ctest_labels_for_subprojects) endif() -add_RunCMake_test_group(CPack "DEB;RPM;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ;Ext") +add_RunCMake_test_group(CPack "DEB;RPM;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ;External") # add a test to make sure symbols are exported from a shared library # for MSVC compilers CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS property is used add_RunCMake_test(AutoExportDll) diff --git a/Tests/RunCMake/CPack/Ext/Helpers.cmake b/Tests/RunCMake/CPack/External/Helpers.cmake index 2c67e06..2c67e06 100644 --- a/Tests/RunCMake/CPack/Ext/Helpers.cmake +++ b/Tests/RunCMake/CPack/External/Helpers.cmake diff --git a/Tests/RunCMake/CPack/Ext/Prerequirements.cmake b/Tests/RunCMake/CPack/External/Prerequirements.cmake index e69de29..e69de29 100644 --- a/Tests/RunCMake/CPack/Ext/Prerequirements.cmake +++ b/Tests/RunCMake/CPack/External/Prerequirements.cmake diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index 91d3cb7..33ddb72 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake @@ -18,8 +18,8 @@ run_cpack_test(GENERATE_SHLIBS_LDCONFIG "DEB" true "COMPONENT") run_cpack_test(INSTALL_SCRIPTS "RPM" false "COMPONENT") run_cpack_test(LONG_FILENAMES "DEB" false "MONOLITHIC") run_cpack_test_subtests(MAIN_COMPONENT "invalid;found" "RPM" false "COMPONENT") -run_cpack_test(MINIMAL "RPM;DEB;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ;Ext" false "MONOLITHIC;COMPONENT") -run_cpack_test_package_target(MINIMAL "RPM;DEB;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ;Ext" false "MONOLITHIC;COMPONENT") +run_cpack_test(MINIMAL "RPM;DEB;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ;External" false "MONOLITHIC;COMPONENT") +run_cpack_test_package_target(MINIMAL "RPM;DEB;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ;External" false "MONOLITHIC;COMPONENT") run_cpack_test_subtests(PACKAGE_CHECKSUM "invalid;MD5;SHA1;SHA224;SHA256;SHA384;SHA512" "TGZ" false "MONOLITHIC") run_cpack_test(PARTIALLY_RELOCATABLE_WARNING "RPM" false "COMPONENT") run_cpack_test(PER_COMPONENT_FIELDS "RPM;DEB" false "COMPONENT") @@ -35,4 +35,4 @@ run_cpack_test(USER_FILELIST "RPM" false "MONOLITHIC") run_cpack_test(MD5SUMS "DEB" false "MONOLITHIC;COMPONENT") run_cpack_test(CPACK_INSTALL_SCRIPT "ZIP" false "MONOLITHIC") run_cpack_test(DEB_PACKAGE_VERSION_BACK_COMPATIBILITY "DEB" false "MONOLITHIC;COMPONENT") -run_cpack_test_subtests(EXT "none;good;good_multi;bad_major;bad_minor;invalid_good;invalid_bad;stage_and_package" "Ext" false "MONOLITHIC;COMPONENT") +run_cpack_test_subtests(EXTERNAL "none;good;good_multi;bad_major;bad_minor;invalid_good;invalid_bad;stage_and_package" "External" false "MONOLITHIC;COMPONENT") diff --git a/Tests/RunCMake/CPack/tests/EXT/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/EXT/VerifyResult.cmake deleted file mode 100644 index 97b74f7..0000000 --- a/Tests/RunCMake/CPack/tests/EXT/VerifyResult.cmake +++ /dev/null @@ -1,3 +0,0 @@ -if(RunCMake_SUBTEST_SUFFIX MATCHES "^(none|good(_multi)?|invalid_good)") - check_ext_json("${src_dir}/tests/EXT/expected-json-1.0.txt" "${FOUND_FILE_1}") -endif() diff --git a/Tests/RunCMake/CPack/tests/EXT/bad_major-stderr.txt b/Tests/RunCMake/CPack/tests/EXT/bad_major-stderr.txt deleted file mode 100644 index 372c5e4..0000000 --- a/Tests/RunCMake/CPack/tests/EXT/bad_major-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error at .*/Modules/Internal/CPack/CPackExt\.cmake:[0-9]+ \(message\): - Could not find a suitable version in CPACK_EXT_REQUESTED_VERSIONS - - -CPack Error: Error while executing CPackExt\.cmake -CPack Error: Cannot initialize the generator Ext diff --git a/Tests/RunCMake/CPack/tests/EXT/bad_minor-stderr.txt b/Tests/RunCMake/CPack/tests/EXT/bad_minor-stderr.txt deleted file mode 100644 index 372c5e4..0000000 --- a/Tests/RunCMake/CPack/tests/EXT/bad_minor-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error at .*/Modules/Internal/CPack/CPackExt\.cmake:[0-9]+ \(message\): - Could not find a suitable version in CPACK_EXT_REQUESTED_VERSIONS - - -CPack Error: Error while executing CPackExt\.cmake -CPack Error: Cannot initialize the generator Ext diff --git a/Tests/RunCMake/CPack/tests/EXT/invalid_bad-stderr.txt b/Tests/RunCMake/CPack/tests/EXT/invalid_bad-stderr.txt deleted file mode 100644 index 372c5e4..0000000 --- a/Tests/RunCMake/CPack/tests/EXT/invalid_bad-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error at .*/Modules/Internal/CPack/CPackExt\.cmake:[0-9]+ \(message\): - Could not find a suitable version in CPACK_EXT_REQUESTED_VERSIONS - - -CPack Error: Error while executing CPackExt\.cmake -CPack Error: Cannot initialize the generator Ext diff --git a/Tests/RunCMake/CPack/tests/EXT/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/EXTERNAL/ExpectedFiles.cmake index 91608c9..91608c9 100644 --- a/Tests/RunCMake/CPack/tests/EXT/ExpectedFiles.cmake +++ b/Tests/RunCMake/CPack/tests/EXTERNAL/ExpectedFiles.cmake diff --git a/Tests/RunCMake/CPack/tests/EXTERNAL/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/EXTERNAL/VerifyResult.cmake new file mode 100644 index 0000000..bc19d7e --- /dev/null +++ b/Tests/RunCMake/CPack/tests/EXTERNAL/VerifyResult.cmake @@ -0,0 +1,3 @@ +if(RunCMake_SUBTEST_SUFFIX MATCHES "^(none|good(_multi)?|invalid_good)") + check_ext_json("${src_dir}/tests/EXTERNAL/expected-json-1.0.txt" "${FOUND_FILE_1}") +endif() diff --git a/Tests/RunCMake/CPack/tests/EXTERNAL/bad_major-stderr.txt b/Tests/RunCMake/CPack/tests/EXTERNAL/bad_major-stderr.txt new file mode 100644 index 0000000..f2e160e --- /dev/null +++ b/Tests/RunCMake/CPack/tests/EXTERNAL/bad_major-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at .*/Modules/Internal/CPack/CPackExternal\.cmake:[0-9]+ \(message\): + Could not find a suitable version in CPACK_EXTERNAL_REQUESTED_VERSIONS + + +CPack Error: Error while executing CPackExternal\.cmake +CPack Error: Cannot initialize the generator External diff --git a/Tests/RunCMake/CPack/tests/EXTERNAL/bad_minor-stderr.txt b/Tests/RunCMake/CPack/tests/EXTERNAL/bad_minor-stderr.txt new file mode 100644 index 0000000..f2e160e --- /dev/null +++ b/Tests/RunCMake/CPack/tests/EXTERNAL/bad_minor-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at .*/Modules/Internal/CPack/CPackExternal\.cmake:[0-9]+ \(message\): + Could not find a suitable version in CPACK_EXTERNAL_REQUESTED_VERSIONS + + +CPack Error: Error while executing CPackExternal\.cmake +CPack Error: Cannot initialize the generator External diff --git a/Tests/RunCMake/CPack/tests/EXT/create_package.cmake b/Tests/RunCMake/CPack/tests/EXTERNAL/create_package.cmake index e308ccb..e308ccb 100644 --- a/Tests/RunCMake/CPack/tests/EXT/create_package.cmake +++ b/Tests/RunCMake/CPack/tests/EXTERNAL/create_package.cmake diff --git a/Tests/RunCMake/CPack/tests/EXT/expected-json-1.0.txt b/Tests/RunCMake/CPack/tests/EXTERNAL/expected-json-1.0.txt index b96cf0b..18bf617 100644 --- a/Tests/RunCMake/CPack/tests/EXT/expected-json-1.0.txt +++ b/Tests/RunCMake/CPack/tests/EXTERNAL/expected-json-1.0.txt @@ -150,8 +150,8 @@ \} \}, "packageDescriptionFile" : ".*/Templates/CPack\.GenericDescription\.txt", - "packageDescriptionSummary" : "EXT-(none|good(_multi)?|invalid_good)-subtest-(MONOLITHIC|COMPONENT)-type built using CMake", - "packageName" : "ext", + "packageDescriptionSummary" : "EXTERNAL-(none|good(_multi)?|invalid_good)-subtest-(MONOLITHIC|COMPONENT)-type built using CMake", + "packageName" : "external", "packageVersion" : "0\.1\.1", "projects" :[ ] \[ @@ -164,9 +164,9 @@ "f3", "f4" \], - "directory" : ".*/Tests/RunCMake/Ext/CPack/EXT-build-(none|good(_multi)?|invalid_good)-subtest", + "directory" : ".*/Tests/RunCMake/External/CPack/EXTERNAL-build-(none|good(_multi)?|invalid_good)-subtest", "installationTypes" : \[\], - "projectName" : "EXT-(none|good(_multi)?|invalid_good)-subtest-(MONOLITHIC|COMPONENT)-type", + "projectName" : "EXTERNAL-(none|good(_multi)?|invalid_good)-subtest-(MONOLITHIC|COMPONENT)-type", "subDirectory" : "/" \} \], diff --git a/Tests/RunCMake/CPack/tests/EXTERNAL/invalid_bad-stderr.txt b/Tests/RunCMake/CPack/tests/EXTERNAL/invalid_bad-stderr.txt new file mode 100644 index 0000000..f2e160e --- /dev/null +++ b/Tests/RunCMake/CPack/tests/EXTERNAL/invalid_bad-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at .*/Modules/Internal/CPack/CPackExternal\.cmake:[0-9]+ \(message\): + Could not find a suitable version in CPACK_EXTERNAL_REQUESTED_VERSIONS + + +CPack Error: Error while executing CPackExternal\.cmake +CPack Error: Cannot initialize the generator External diff --git a/Tests/RunCMake/CPack/tests/EXT/stage_and_package-stderr.txt b/Tests/RunCMake/CPack/tests/EXTERNAL/stage_and_package-stderr.txt index 40f2743..40f2743 100644 --- a/Tests/RunCMake/CPack/tests/EXT/stage_and_package-stderr.txt +++ b/Tests/RunCMake/CPack/tests/EXTERNAL/stage_and_package-stderr.txt diff --git a/Tests/RunCMake/CPack/tests/EXT/test.cmake b/Tests/RunCMake/CPack/tests/EXTERNAL/test.cmake index 976cb6a..bc9766b 100644 --- a/Tests/RunCMake/CPack/tests/EXT/test.cmake +++ b/Tests/RunCMake/CPack/tests/EXTERNAL/test.cmake @@ -1,22 +1,22 @@ include(CPackComponent) if(RunCMake_SUBTEST_SUFFIX STREQUAL "none") - unset(CPACK_EXT_REQUESTED_VERSIONS) + unset(CPACK_EXTERNAL_REQUESTED_VERSIONS) elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "good") - set(CPACK_EXT_REQUESTED_VERSIONS "1.0") + set(CPACK_EXTERNAL_REQUESTED_VERSIONS "1.0") elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "good_multi") - set(CPACK_EXT_REQUESTED_VERSIONS "1.0;2.0") + set(CPACK_EXTERNAL_REQUESTED_VERSIONS "1.0;2.0") elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "bad_major") - set(CPACK_EXT_REQUESTED_VERSIONS "2.0") + set(CPACK_EXTERNAL_REQUESTED_VERSIONS "2.0") elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "bad_minor") - set(CPACK_EXT_REQUESTED_VERSIONS "1.1") + set(CPACK_EXTERNAL_REQUESTED_VERSIONS "1.1") elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "invalid_good") - set(CPACK_EXT_REQUESTED_VERSIONS "1;1.0") + set(CPACK_EXTERNAL_REQUESTED_VERSIONS "1;1.0") elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "invalid_bad") - set(CPACK_EXT_REQUESTED_VERSIONS "1") + set(CPACK_EXTERNAL_REQUESTED_VERSIONS "1") elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "stage_and_package") - set(CPACK_EXT_ENABLE_STAGING 1) - set(CPACK_EXT_PACKAGE_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/create_package.cmake") + set(CPACK_EXTERNAL_ENABLE_STAGING 1) + set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/create_package.cmake") endif() file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/f1.txt" test1) diff --git a/Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake b/Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake index fa5618a..ab3e51b 100644 --- a/Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake +++ b/Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake @@ -1,5 +1,13 @@ include(RunCMake) +function(run_TargetWithCommand) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TargetWithCommand-build) + run_cmake(TargetWithCommand) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(TargetWithCommand-build ${CMAKE_COMMAND} --build . --config Debug) +endfunction() +run_TargetWithCommand() + # Use a single build tree for a few tests without cleaning. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CommandWithOutput-build) set(RunCMake_TEST_NO_CLEAN 1) diff --git a/Tests/RunCMake/CSharpCustomCommand/TargetWithCommand-build-stdout.txt b/Tests/RunCMake/CSharpCustomCommand/TargetWithCommand-build-stdout.txt new file mode 100644 index 0000000..c212a8f --- /dev/null +++ b/Tests/RunCMake/CSharpCustomCommand/TargetWithCommand-build-stdout.txt @@ -0,0 +1 @@ +Custom target with CSharp source diff --git a/Tests/RunCMake/CSharpCustomCommand/TargetWithCommand.cmake b/Tests/RunCMake/CSharpCustomCommand/TargetWithCommand.cmake new file mode 100644 index 0000000..fdaea5c --- /dev/null +++ b/Tests/RunCMake/CSharpCustomCommand/TargetWithCommand.cmake @@ -0,0 +1,4 @@ +enable_language(CSharp) + +add_custom_target(drive ALL SOURCES dummy.cs + COMMAND ${CMAKE_COMMAND} -E echo "Custom target with CSharp source") diff --git a/Tests/RunCMake/target_compile_definitions/CMakeLists.txt b/Tests/RunCMake/target_compile_definitions/CMakeLists.txt new file mode 100644 index 0000000..14ef56e --- /dev/null +++ b/Tests/RunCMake/target_compile_definitions/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.11) + +project(${RunCMake_TEST} LANGUAGES NONE) + +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/target_compile_definitions/RunCMakeTest.cmake b/Tests/RunCMake/target_compile_definitions/RunCMakeTest.cmake new file mode 100644 index 0000000..b67c598 --- /dev/null +++ b/Tests/RunCMake/target_compile_definitions/RunCMakeTest.cmake @@ -0,0 +1,3 @@ +include(RunCMake) + +run_cmake(empty_keyword_args) diff --git a/Tests/RunCMake/target_compile_definitions/empty_keyword_args.cmake b/Tests/RunCMake/target_compile_definitions/empty_keyword_args.cmake new file mode 100644 index 0000000..cb94e87 --- /dev/null +++ b/Tests/RunCMake/target_compile_definitions/empty_keyword_args.cmake @@ -0,0 +1,5 @@ +add_library(iface INTERFACE) +target_compile_definitions(iface PUBLIC PRIVATE INTERFACE) +# Cannot be called with non-compilable targets. +#add_library(imported UNKNOWN IMPORTED) +#target_compile_definitions(imported PUBLIC PRIVATE INTERFACE) diff --git a/Tests/RunCMake/target_compile_features/RunCMakeTest.cmake b/Tests/RunCMake/target_compile_features/RunCMakeTest.cmake index 1f67f11..f8b0809 100644 --- a/Tests/RunCMake/target_compile_features/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_compile_features/RunCMakeTest.cmake @@ -12,3 +12,4 @@ run_cmake(no_matching_cxx_feature) run_cmake(not_a_c_feature) run_cmake(no_matching_c_feature) run_cmake(cxx_not_enabled) +run_cmake(empty_keyword_args) diff --git a/Tests/RunCMake/target_compile_features/empty_keyword_args.cmake b/Tests/RunCMake/target_compile_features/empty_keyword_args.cmake new file mode 100644 index 0000000..8d57c1c --- /dev/null +++ b/Tests/RunCMake/target_compile_features/empty_keyword_args.cmake @@ -0,0 +1,5 @@ +add_library(iface INTERFACE) +target_compile_features(iface PUBLIC PRIVATE INTERFACE) +# Cannot be called with non-compilable targets. +#add_library(imported UNKNOWN IMPORTED) +#target_compile_features(imported PUBLIC PRIVATE INTERFACE) diff --git a/Tests/RunCMake/target_compile_options/CMakeLists.txt b/Tests/RunCMake/target_compile_options/CMakeLists.txt new file mode 100644 index 0000000..14ef56e --- /dev/null +++ b/Tests/RunCMake/target_compile_options/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.11) + +project(${RunCMake_TEST} LANGUAGES NONE) + +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/target_compile_options/RunCMakeTest.cmake b/Tests/RunCMake/target_compile_options/RunCMakeTest.cmake new file mode 100644 index 0000000..b67c598 --- /dev/null +++ b/Tests/RunCMake/target_compile_options/RunCMakeTest.cmake @@ -0,0 +1,3 @@ +include(RunCMake) + +run_cmake(empty_keyword_args) diff --git a/Tests/RunCMake/target_compile_options/empty_keyword_args.cmake b/Tests/RunCMake/target_compile_options/empty_keyword_args.cmake new file mode 100644 index 0000000..8b92fcf --- /dev/null +++ b/Tests/RunCMake/target_compile_options/empty_keyword_args.cmake @@ -0,0 +1,5 @@ +add_library(iface INTERFACE) +target_compile_options(iface PUBLIC PRIVATE INTERFACE) +# Cannot be called with non-compilable targets. +#add_library(imported UNKNOWN IMPORTED) +#target_compile_options(imported PUBLIC PRIVATE INTERFACE) diff --git a/Tests/RunCMake/target_include_directories/CMakeLists.txt b/Tests/RunCMake/target_include_directories/CMakeLists.txt new file mode 100644 index 0000000..14ef56e --- /dev/null +++ b/Tests/RunCMake/target_include_directories/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.11) + +project(${RunCMake_TEST} LANGUAGES NONE) + +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/target_include_directories/RunCMakeTest.cmake b/Tests/RunCMake/target_include_directories/RunCMakeTest.cmake new file mode 100644 index 0000000..b67c598 --- /dev/null +++ b/Tests/RunCMake/target_include_directories/RunCMakeTest.cmake @@ -0,0 +1,3 @@ +include(RunCMake) + +run_cmake(empty_keyword_args) diff --git a/Tests/RunCMake/target_include_directories/empty_keyword_args.cmake b/Tests/RunCMake/target_include_directories/empty_keyword_args.cmake new file mode 100644 index 0000000..08eaf91 --- /dev/null +++ b/Tests/RunCMake/target_include_directories/empty_keyword_args.cmake @@ -0,0 +1,5 @@ +add_library(iface INTERFACE) +target_include_directories(iface PUBLIC PRIVATE INTERFACE) +# Cannot be called with non-compilable targets. +#add_library(imported UNKNOWN IMPORTED) +#target_include_directories(imported PUBLIC PRIVATE INTERFACE) diff --git a/Tests/RunCMake/target_link_directories/CMakeLists.txt b/Tests/RunCMake/target_link_directories/CMakeLists.txt new file mode 100644 index 0000000..14ef56e --- /dev/null +++ b/Tests/RunCMake/target_link_directories/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.11) + +project(${RunCMake_TEST} LANGUAGES NONE) + +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/target_link_directories/RunCMakeTest.cmake b/Tests/RunCMake/target_link_directories/RunCMakeTest.cmake new file mode 100644 index 0000000..b67c598 --- /dev/null +++ b/Tests/RunCMake/target_link_directories/RunCMakeTest.cmake @@ -0,0 +1,3 @@ +include(RunCMake) + +run_cmake(empty_keyword_args) diff --git a/Tests/RunCMake/target_link_directories/empty_keyword_args.cmake b/Tests/RunCMake/target_link_directories/empty_keyword_args.cmake new file mode 100644 index 0000000..aadf80a --- /dev/null +++ b/Tests/RunCMake/target_link_directories/empty_keyword_args.cmake @@ -0,0 +1,5 @@ +add_library(iface INTERFACE) +target_link_directories(iface PUBLIC PRIVATE INTERFACE) +# Cannot be called with non-compilable targets. +#add_library(imported UNKNOWN IMPORTED) +#target_link_directories(imported PUBLIC PRIVATE INTERFACE) diff --git a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake index a041d6d..0152d4c 100644 --- a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake @@ -19,3 +19,4 @@ run_cmake(SharedDepNotTarget) run_cmake(StaticPrivateDepNotExported) run_cmake(StaticPrivateDepNotTarget) run_cmake(UNKNOWN-IMPORTED-GLOBAL) +run_cmake(empty_keyword_args) diff --git a/Tests/RunCMake/target_link_libraries/empty_keyword_args.cmake b/Tests/RunCMake/target_link_libraries/empty_keyword_args.cmake new file mode 100644 index 0000000..440fa06 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries/empty_keyword_args.cmake @@ -0,0 +1,4 @@ +add_library(iface INTERFACE) +target_link_libraries(iface PUBLIC PRIVATE INTERFACE) +add_library(imported UNKNOWN IMPORTED) +target_link_libraries(imported PUBLIC PRIVATE INTERFACE) diff --git a/Tests/RunCMake/target_link_options/RunCMakeTest.cmake b/Tests/RunCMake/target_link_options/RunCMakeTest.cmake index 1eaa5d2..1d9ef8b 100644 --- a/Tests/RunCMake/target_link_options/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_options/RunCMakeTest.cmake @@ -39,3 +39,5 @@ if(RunCMake_GENERATOR MATCHES "(Ninja|Makefile)") run_cmake_target(LINKER_expansion LINKER linker) run_cmake_target(LINKER_expansion LINKER_SHELL linker_shell) endif() + +run_cmake(empty_keyword_args) diff --git a/Tests/RunCMake/target_link_options/empty_keyword_args.cmake b/Tests/RunCMake/target_link_options/empty_keyword_args.cmake new file mode 100644 index 0000000..a1a297e --- /dev/null +++ b/Tests/RunCMake/target_link_options/empty_keyword_args.cmake @@ -0,0 +1,5 @@ +add_library(iface INTERFACE) +target_link_options(iface PUBLIC PRIVATE INTERFACE) +# Cannot be called with non-compilable targets. +#add_library(imported UNKNOWN IMPORTED) +#target_link_options(imported PUBLIC PRIVATE INTERFACE) diff --git a/Tests/RunCMake/target_sources/CMakeLists.txt b/Tests/RunCMake/target_sources/CMakeLists.txt new file mode 100644 index 0000000..14ef56e --- /dev/null +++ b/Tests/RunCMake/target_sources/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.11) + +project(${RunCMake_TEST} LANGUAGES NONE) + +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/target_sources/RunCMakeTest.cmake b/Tests/RunCMake/target_sources/RunCMakeTest.cmake new file mode 100644 index 0000000..b67c598 --- /dev/null +++ b/Tests/RunCMake/target_sources/RunCMakeTest.cmake @@ -0,0 +1,3 @@ +include(RunCMake) + +run_cmake(empty_keyword_args) diff --git a/Tests/RunCMake/target_sources/empty_keyword_args.cmake b/Tests/RunCMake/target_sources/empty_keyword_args.cmake new file mode 100644 index 0000000..5cee451 --- /dev/null +++ b/Tests/RunCMake/target_sources/empty_keyword_args.cmake @@ -0,0 +1,5 @@ +add_library(iface INTERFACE) +target_sources(iface PUBLIC PRIVATE INTERFACE) +# Cannot be called with non-compilable targets. +#add_library(imported UNKNOWN IMPORTED) +#target_sources(imported PUBLIC PRIVATE INTERFACE) diff --git a/Utilities/.gitattributes b/Utilities/.gitattributes index 96a4323..81bbf26 100644 --- a/Utilities/.gitattributes +++ b/Utilities/.gitattributes @@ -5,3 +5,4 @@ SetupForDevelopment.sh export-ignore # Do not format third-party sources. /KWIML/** -format.clang-format-6.0 /cm*/** -format.clang-format-6.0 +/cmcurl/curltest.c format.clang-format-6.0 diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index 3d31e38..fb46052 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash @@ -8,7 +8,7 @@ readonly name="curl" readonly ownership="Curl Upstream <curl-library@cool.haxx.se>" readonly subtree="Utilities/cmcurl" readonly repo="https://github.com/curl/curl.git" -readonly tag="curl-7_61_1" +readonly tag="curl-7_62_0" readonly shortlog=false readonly paths=" CMake/* diff --git a/Utilities/cmcurl/CMake/CMakeConfigurableFile.in b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in index 4cf74a1..df2c382 100644 --- a/Utilities/cmcurl/CMake/CMakeConfigurableFile.in +++ b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in @@ -1,2 +1 @@ @CMAKE_CONFIGURABLE_FILE_CONTENT@ - diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c index ab244ac..9388c83 100644 --- a/Utilities/cmcurl/CMake/CurlTests.c +++ b/Utilities/cmcurl/CMake/CurlTests.c @@ -549,3 +549,19 @@ main() { return 0; } #endif +#ifdef HAVE_CLOCK_GETTIME_MONOTONIC +#include <time.h> +int +main() { + struct timespec ts = {0, 0}; + clock_gettime(CLOCK_MONOTONIC, &ts); + return 0; +} +#endif +#ifdef HAVE_BUILTIN_AVAILABLE +int +main() { + if(__builtin_available(macOS 10.12, *)) {} + return 0; +} +#endif diff --git a/Utilities/cmcurl/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in index 73e04c6..1294e17 100644 --- a/Utilities/cmcurl/CMake/curl-config.cmake.in +++ b/Utilities/cmcurl/CMake/curl-config.cmake.in @@ -1,64 +1,12 @@ - -get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -if(NOT CURL_FIND_COMPONENTS) - set(CURL_FIND_COMPONENTS curl libcurl) - if(CURL_FIND_REQUIRED) - set(CURL_FIND_REQUIRED_curl TRUE) - set(CURL_FIND_REQUIRED_libcurl TRUE) - endif() -endif() +@PACKAGE_INIT@ include(CMakeFindDependencyMacro) -if(CURL_FIND_REQUIRED_libcurl) - find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@") +if(@USE_OPENSSL@) + find_dependency(OpenSSL @OPENSSL_VERSION_MAJOR@) endif() - -set(_curl_missing_components) -foreach(_comp ${CURL_FIND_COMPONENTS}) - if(EXISTS "${_DIR}/${_comp}-target.cmake") - include("${_DIR}/${_comp}-target.cmake") - set(CURL_${_comp}_FOUND TRUE) - else() - set(CURL_${_comp}_FOUND FALSE) - if(CURL_FIND_REQUIRED_${_comp}) - set(CURL_FOUND FALSE) - list(APPEND _curl_missing_components ${_comp}) - endif() - endif() -endforeach() - -if(_curl_missing_components) - set(CURL_NOT_FOUND_MESSAGE "Following required components not found: " ${_curl_missing_components}) -else() - if(TARGET CURL::libcurl) - string(TOUPPER "${CMAKE_BUILD_TYPE}" _curl_current_config) - if(NOT _curl_current_config) - set(_curl_current_config "NOCONFIG") - endif() - get_target_property(_curl_configurations CURL::libcurl IMPORTED_CONFIGURATIONS) - list(FIND _curl_configurations "${_curl_current_config}" _i) - if(_i LESS 0) - set(_curl_config "RELEASE") - list(FIND _curl_configurations "${_curl_current_config}" _i) - if(_i LESS 0) - set(_curl_config "NOCONFIG") - list(FIND _curl_configurations "${_curl_current_config}" _i) - endif() - endif() - - if(_i LESS 0) - set(_curl_current_config "") # let CMake pick config at random - else() - set(_curl_current_config "_${_curl_current_config}") - endif() - - get_target_property(CURL_INCLUDE_DIRS CURL::libcurl INTERFACE_INCLUDE_DIRECTORIES) - get_target_property(CURL_LIBRARIES CURL::libcurl "LOCATION${_curl_current_config}") - set(_curl_current_config) - set(_curl_configurations) - set(_i) - endif() +if(@USE_ZLIB@) + find_dependency(ZLIB @ZLIB_VERSION_MAJOR@) endif() -unset(_curl_missing_components) +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index f38b7d4..9ae7aa3 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -180,7 +180,7 @@ option(ENABLE_CURLDEBUG "Set to ON to build with TrackMemory feature enabled" OF if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) if(PICKY_COMPILER) - 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) + 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}) @@ -611,15 +611,23 @@ if(0) # This code not needed for building within CMake. option(CURL_ZLIB "Set to ON to enable building curl with zlib support." ON) set(HAVE_LIBZ OFF) set(HAVE_ZLIB_H OFF) -set(HAVE_ZLIB OFF) +set(USE_ZLIB OFF) if(CURL_ZLIB) find_package(ZLIB QUIET) if(ZLIB_FOUND) set(HAVE_ZLIB_H ON) - set(HAVE_ZLIB ON) set(HAVE_LIBZ ON) - list(APPEND CURL_LIBS ${ZLIB_LIBRARIES}) - include_directories(${ZLIB_INCLUDE_DIRS}) + set(USE_ZLIB ON) + + # Depend on ZLIB via imported targets if supported by the running + # version of CMake. This allows our dependents to get our dependencies + # transitively. + if(NOT CMAKE_VERSION VERSION_LESS 3.4) + list(APPEND CURL_LIBS ZLIB::ZLIB) + else() + list(APPEND CURL_LIBS ${ZLIB_LIBRARIES}) + include_directories(${ZLIB_INCLUDE_DIRS}) + endif() list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS}) endif() endif() @@ -1070,7 +1078,6 @@ foreach(CURL_TEST HAVE_GETHOSTBYNAME_R_3_REENTRANT HAVE_GETHOSTBYNAME_R_5_REENTRANT HAVE_GETHOSTBYNAME_R_6_REENTRANT - HAVE_SOCKLEN_T HAVE_IN_ADDR_T HAVE_BOOL_T STDC_HEADERS @@ -1140,6 +1147,12 @@ if(HAVE_INET_NTOA_R_DECL_REENTRANT) set(NEED_REENTRANT 1) endif() +# Check clock_gettime(CLOCK_MONOTONIC, x) support +curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC) + +# Check compiler support of __builtin_available() +curl_internal_test(HAVE_BUILTIN_AVAILABLE) + # Some other minor tests if(NOT HAVE_IN_ADDR_T) @@ -1189,24 +1202,6 @@ if(CMAKE_COMPILER_IS_GNUCC AND APPLE) endif() endif() -if(HAVE_SOCKLEN_T) - set(CURL_HAVE_SOCKLEN_T 1) - set(CURL_TYPEOF_CURL_SOCKLEN_T "socklen_t") - if(WIN32) - set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h;ws2tcpip.h") - elseif(HAVE_SYS_SOCKET_H) - set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") - endif() - check_type_size("socklen_t" CURL_SIZEOF_CURL_SOCKLEN_T) - set(CMAKE_EXTRA_INCLUDE_FILES) - if(NOT HAVE_CURL_SIZEOF_CURL_SOCKLEN_T) - message(FATAL_ERROR - "Check for sizeof socklen_t failed, see CMakeFiles/CMakerror.log") - endif() -else() - set(CURL_HAVE_SOCKLEN_T 0) -endif() - # TODO test which of these headers are required if(WIN32) set(CURL_PULL_WS2TCPIP_H ${HAVE_WS2TCPIP_H}) @@ -1267,10 +1262,14 @@ function(TRANSFORM_MAKEFILE_INC INPUT_FILE OUTPUT_FILE) endfunction() -if(WIN32 AND NOT CYGWIN) - set(CURL_INSTALL_CMAKE_DIR CMake) -else() - set(CURL_INSTALL_CMAKE_DIR lib/cmake/curl) +if(0) # This code not needed for building within CMake. +include(GNUInstallDirs) + +set(CURL_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) +set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") +set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") +set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") +set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") endif() if(USE_MANUAL) @@ -1285,11 +1284,11 @@ endif() #----------------------------------------------------------------------------- # CMake-specific curl code. -add_executable(LIBCURL curltest.c) -target_link_libraries(LIBCURL cmcurl) +add_executable(curltest curltest.c) +target_link_libraries(curltest cmcurl) if(BUILD_TESTING AND CMAKE_CURL_TEST_URL) - add_test(curl LIBCURL ${CMAKE_CURL_TEST_URL}) + add_test(curl curltest ${CMAKE_CURL_TEST_URL}) endif() install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmcurl) @@ -1418,7 +1417,7 @@ set(VERSIONNUM "${CURL_VERSION_NUM}") configure_file("${CURL_SOURCE_DIR}/curl-config.in" "${CURL_BINARY_DIR}/curl-config" @ONLY) install(FILES "${CURL_BINARY_DIR}/curl-config" - DESTINATION bin + DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE @@ -1428,34 +1427,36 @@ install(FILES "${CURL_BINARY_DIR}/curl-config" configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in" "${CURL_BINARY_DIR}/libcurl.pc" @ONLY) install(FILES "${CURL_BINARY_DIR}/libcurl.pc" - DESTINATION lib/pkgconfig) - -# This needs to be run very last so other parts of the scripts can take advantage of this. -if(NOT CURL_CONFIG_HAS_BEEN_RUN_BEFORE) - set(CURL_CONFIG_HAS_BEEN_RUN_BEFORE 1 CACHE INTERNAL "Flag to track whether this is the first time running CMake or if CMake has been configured before") -endif() + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) # install headers install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl" - DESTINATION include + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h") - include(CMakePackageConfigHelpers) write_basic_package_version_file( - "${PROJECT_BINARY_DIR}/curl-config-version.cmake" + "${version_config}" VERSION ${CURL_VERSION} COMPATIBILITY SameMajorVersion ) -configure_file(CMake/curl-config.cmake.in - "${PROJECT_BINARY_DIR}/curl-config.cmake" - @ONLY +# Use: +# * TARGETS_EXPORT_NAME +# * PROJECT_NAME +configure_package_config_file(CMake/curl-config.cmake.in + "${project_config}" + INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR} +) + +install( + EXPORT "${TARGETS_EXPORT_NAME}" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION ${CURL_INSTALL_CMAKE_DIR} ) install( - FILES ${PROJECT_BINARY_DIR}/curl-config.cmake - ${PROJECT_BINARY_DIR}/curl-config-version.cmake + FILES ${version_config} ${project_config} DESTINATION ${CURL_INSTALL_CMAKE_DIR} ) diff --git a/Utilities/cmcurl/curltest.c b/Utilities/cmcurl/curltest.c index 210868e..f80e758 100644 --- a/Utilities/cmcurl/curltest.c +++ b/Utilities/cmcurl/curltest.c @@ -1,159 +1,81 @@ -/* Prevent warnings on Visual Studio */ -struct _RPC_ASYNC_STATE; - #include "curl/curl.h" + +#include <stdio.h> #include <stdlib.h> #include <string.h> -int GetFtpFile(void) +int test_curl(const char* url) { - int retVal = 0; - CURL *curl; - CURLcode res; - curl = curl_easy_init(); - if(curl) - { - /* Get curl 7.9.2 from sunet.se's FTP site: */ - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); - curl_easy_setopt(curl, CURLOPT_HEADER, 1); - curl_easy_setopt(curl, CURLOPT_URL, - "ftp://public.kitware.com/pub/cmake/cygwin/setup.hint"); - res = curl_easy_perform(curl); - if ( res != 0 ) - { - printf("Error fetching: ftp://public.kitware.com/pub/cmake/cygwin/setup.hint\n"); - retVal = 1; - } - - /* always cleanup */ - curl_easy_cleanup(curl); - } - else - { - printf("Cannot create curl object\n"); - retVal = 1; - } - return retVal; -} - -int GetWebFiles(char *url1, char *url2) -{ - int retVal = 0; - CURL *curl; - CURLcode res; - + CURL* curl; + CURLcode r; char proxy[1024]; int proxy_type = 0; - if ( getenv("HTTP_PROXY") ) - { + if (getenv("HTTP_PROXY")) { proxy_type = 1; - if (getenv("HTTP_PROXY_PORT") ) - { + if (getenv("HTTP_PROXY_PORT")) { sprintf(proxy, "%s:%s", getenv("HTTP_PROXY"), getenv("HTTP_PROXY_PORT")); - } - else - { + } else { sprintf(proxy, "%s", getenv("HTTP_PROXY")); - } - if ( getenv("HTTP_PROXY_TYPE") ) - { + } + if (getenv("HTTP_PROXY_TYPE")) { /* HTTP/SOCKS4/SOCKS5 */ - if ( strcmp(getenv("HTTP_PROXY_TYPE"), "HTTP") == 0 ) - { + if (strcmp(getenv("HTTP_PROXY_TYPE"), "HTTP") == 0) { proxy_type = 1; - } - else if ( strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS4") == 0 ) - { + } else if (strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS4") == 0) { proxy_type = 2; - } - else if ( strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS5") == 0 ) - { + } else if (strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS5") == 0) { proxy_type = 3; - } } } + } curl = curl_easy_init(); - if(curl) - { - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); - curl_easy_setopt(curl, CURLOPT_HEADER, 1); - - /* Using proxy */ - if ( proxy_type > 0 ) - { - curl_easy_setopt(curl, CURLOPT_PROXY, proxy); - switch (proxy_type) - { - case 2: - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); - break; - case 3: - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - break; - default: - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - } - } - - /* get the first document */ - curl_easy_setopt(curl, CURLOPT_URL, url1); - res = curl_easy_perform(curl); - if ( res != 0 ) - { - printf("Error fetching: %s\n", url1); - retVal = 1; - } + if (!curl) { + fprintf(stderr, "curl_easy_init failed\n"); + return 1; + } + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + curl_easy_setopt(curl, CURLOPT_HEADER, 1); + + if (proxy_type > 0) { + curl_easy_setopt(curl, CURLOPT_PROXY, proxy); + switch (proxy_type) { + case 2: + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); + break; + case 3: + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + break; + default: + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + } - /* get another document from the same server using the same - connection */ - /* avoid warnings about url2 since below block is commented out: */ - (void) url2; - /* - curl_easy_setopt(curl, CURLOPT_URL, url2); - res = curl_easy_perform(curl); - if ( res != 0 ) - { - printf("Error fetching: %s\n", url2); - retVal = 1; - } - */ + curl_easy_setopt(curl, CURLOPT_URL, url); + r = curl_easy_perform(curl); + curl_easy_cleanup(curl); - /* always cleanup */ - curl_easy_cleanup(curl); - } - else - { - printf("Cannot create curl object\n"); - retVal = 1; - } + if (r != CURLE_OK) { + fprintf(stderr, "error: fetching '%s' failed: %s\n", url, + curl_easy_strerror(r)); + return 1; + } - return retVal; + return 0; } - -int main(int argc, char **argv) +int main(int argc, const char* argv[]) { - int retVal = 0; - + int r; curl_global_init(CURL_GLOBAL_DEFAULT); - - if(argc>1) - { - retVal += GetWebFiles(argv[1], 0); - } - else - { - printf("error: first argument should be a url to download\n"); - retVal = 1; - } - - /* Do not check the output of FTP socks5 cannot handle FTP yet */ - /* GetFtpFile(); */ - /* do not test ftp right now because we don't enable that port */ - + if (argc == 2) { + r = test_curl(argv[1]); + } else { + fprintf(stderr, "error: no URL given as first argument\n"); + r = 1; + } curl_global_cleanup(); - - return retVal; + return r; } diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index d9955bd..63bb291 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h @@ -146,7 +146,8 @@ typedef enum { CURLSSLBACKEND_SCHANNEL = 8, CURLSSLBACKEND_DARWINSSL = 9, CURLSSLBACKEND_AXTLS = 10, - CURLSSLBACKEND_MBEDTLS = 11 + CURLSSLBACKEND_MBEDTLS = 11, + CURLSSLBACKEND_MESALINK = 12 } curl_sslbackend; /* aliases for library clones and renames */ @@ -517,8 +518,7 @@ typedef enum { CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ CURLE_TELNET_OPTION_SYNTAX, /* 49 - Malformed telnet option */ CURLE_OBSOLETE50, /* 50 - NOT USED */ - CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint - wasn't verified fine */ + CURLE_OBSOLETE51, /* 51 - NOT USED */ CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as @@ -528,7 +528,8 @@ typedef enum { CURLE_OBSOLETE57, /* 57 - NOT IN USE */ CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ - CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_PEER_FAILED_VERIFICATION, /* 60 - peer's certificate or fingerprint + wasn't verified fine */ CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ @@ -584,6 +585,9 @@ typedef enum { CURL_LAST /* never use! */ } CURLcode; +/* added in 7.62.0 */ +#define CURLE_SSL_CACERT CURLE_PEER_FAILED_VERIFICATION + #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ @@ -800,6 +804,9 @@ typedef enum { this value, keep them in sync. */ #define CURL_HET_DEFAULT 200L +/* The default connection upkeep interval in milliseconds. */ +#define CURL_UPKEEP_INTERVAL_DEFAULT 60000L + #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ @@ -1856,6 +1863,15 @@ typedef enum { /* Disallow specifying username/login in URL. */ CINIT(DISALLOW_USERNAME_IN_URL, LONG, 278), + /* DNS-over-HTTPS URL */ + CINIT(DOH_URL, STRINGPOINT, 279), + + /* Preferred buffer size to use for uploads */ + CINIT(UPLOAD_BUFFERSIZE, LONG, 280), + + /* Time in ms between connection upkeep calls for long-lived connections. */ + CINIT(UPKEEP_INTERVAL_MS, LONG, 281), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -2779,6 +2795,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); stuff before they can be included! */ #include "easy.h" /* nothing in curl is fun without the easy stuff */ #include "multi.h" +#include "urlapi.h" /* the typechecker doesn't work in C++ (yet) */ #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index e266f18..3d7b1fb 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h @@ -30,13 +30,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.61.1" +#define LIBCURL_VERSION "7.62.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 61 -#define LIBCURL_VERSION_PATCH 1 +#define LIBCURL_VERSION_MINOR 62 +#define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparions by programs. The LIBCURL_VERSION_NUM define will @@ -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 0x073D01 +#define LIBCURL_VERSION_NUM 0x073E00 /* * This is the date and time when the full source package was created. The diff --git a/Utilities/cmcurl/include/curl/easy.h b/Utilities/cmcurl/include/curl/easy.h index 752c504..f42a8a9 100644 --- a/Utilities/cmcurl/include/curl/easy.h +++ b/Utilities/cmcurl/include/curl/easy.h @@ -95,6 +95,16 @@ CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, size_t *n); + +/* + * NAME curl_easy_upkeep() + * + * DESCRIPTION + * + * Performs connection upkeep for the given session handle. + */ +CURL_EXTERN CURLcode curl_easy_upkeep(CURL *curl); + #ifdef __cplusplus } #endif diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h index a54fd58..1e555ec 100644 --- a/Utilities/cmcurl/include/curl/system.h +++ b/Utilities/cmcurl/include/curl/system.h @@ -298,7 +298,7 @@ # define CURL_PULL_SYS_TYPES_H 1 # define CURL_PULL_SYS_SOCKET_H 1 -#elif defined(__SUNPRO_C) /* Oracle Solaris Studio */ +#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) /* Oracle Solaris Studio */ # if !defined(__LP64) && (defined(__ILP32) || \ defined(__i386) || \ defined(__sparcv8) || \ diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h index a6f6386..2443362 100644 --- a/Utilities/cmcurl/include/curl/typecheck-gcc.h +++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -269,6 +269,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_DNS_LOCAL_IP4 || \ (option) == CURLOPT_DNS_LOCAL_IP6 || \ (option) == CURLOPT_DNS_SERVERS || \ + (option) == CURLOPT_DOH_URL || \ (option) == CURLOPT_EGDSOCKET || \ (option) == CURLOPT_FTPPORT || \ (option) == CURLOPT_FTP_ACCOUNT || \ @@ -500,7 +501,8 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, /* 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), char) || \ + _curl_is_arr((expr), unsigned char)) /* FIXME: the whole callback checking is messy... * The idea is to tolerate char vs. void and const vs. not const diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h new file mode 100644 index 0000000..90dd56c --- /dev/null +++ b/Utilities/cmcurl/include/curl/urlapi.h @@ -0,0 +1,120 @@ +#ifndef __CURL_URLAPI_H +#define __CURL_URLAPI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.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 __cplusplus +extern "C" { +#endif + +/* the error codes for the URL API */ +typedef enum { + CURLUE_OK, + CURLUE_BAD_HANDLE, /* 1 */ + CURLUE_BAD_PARTPOINTER, /* 2 */ + CURLUE_MALFORMED_INPUT, /* 3 */ + CURLUE_BAD_PORT_NUMBER, /* 4 */ + CURLUE_UNSUPPORTED_SCHEME, /* 5 */ + CURLUE_URLDECODE, /* 6 */ + CURLUE_OUT_OF_MEMORY, /* 7 */ + CURLUE_USER_NOT_ALLOWED, /* 8 */ + CURLUE_UNKNOWN_PART, /* 9 */ + CURLUE_NO_SCHEME, /* 10 */ + CURLUE_NO_USER, /* 11 */ + CURLUE_NO_PASSWORD, /* 12 */ + CURLUE_NO_OPTIONS, /* 13 */ + CURLUE_NO_HOST, /* 14 */ + CURLUE_NO_PORT, /* 15 */ + CURLUE_NO_QUERY, /* 16 */ + CURLUE_NO_FRAGMENT /* 17 */ +} CURLUcode; + +typedef enum { + CURLUPART_URL, + CURLUPART_SCHEME, + CURLUPART_USER, + CURLUPART_PASSWORD, + CURLUPART_OPTIONS, + CURLUPART_HOST, + CURLUPART_PORT, + CURLUPART_PATH, + CURLUPART_QUERY, + CURLUPART_FRAGMENT +} CURLUPart; + +#define CURLU_DEFAULT_PORT (1<<0) /* return default port number */ +#define CURLU_NO_DEFAULT_PORT (1<<1) /* act as if no port number was set, + if the port number matches the + default for the scheme */ +#define CURLU_DEFAULT_SCHEME (1<<2) /* return default scheme if + missing */ +#define CURLU_NON_SUPPORT_SCHEME (1<<3) /* allow non-supported scheme */ +#define CURLU_PATH_AS_IS (1<<4) /* leave dot sequences */ +#define CURLU_DISALLOW_USER (1<<5) /* no user+password allowed */ +#define CURLU_URLDECODE (1<<6) /* URL decode on get */ +#define CURLU_URLENCODE (1<<7) /* URL encode on set */ +#define CURLU_APPENDQUERY (1<<8) /* append a form style part */ +#define CURLU_GUESS_SCHEME (1<<9) /* legacy curl-style guessing */ + +typedef struct Curl_URL CURLU; + +/* + * curl_url() creates a new CURLU handle and returns a pointer to it. + * Must be freed with curl_url_cleanup(). + */ +CURL_EXTERN CURLU *curl_url(void); + +/* + * curl_url_cleanup() frees the CURLU handle and related resources used for + * the URL parsing. It will not free strings previously returned with the URL + * API. + */ +CURL_EXTERN void curl_url_cleanup(CURLU *handle); + +/* + * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new + * handle must also be freed with curl_url_cleanup(). + */ +CURL_EXTERN CURLU *curl_url_dup(CURLU *in); + +/* + * curl_url_get() extracts a specific part of the URL from a CURLU + * handle. Returns error code. The returned pointer MUST be freed with + * curl_free() afterwards. + */ +CURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what, + char **part, unsigned int flags); + +/* + * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns + * error code. The passed in string will be copied. Passing a NULL instead of + * a part string, clears that part. + */ +CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, + const char *part, unsigned int flags); + + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index 3d52105..2a6279c 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -129,13 +129,14 @@ if(WIN32) endif() target_include_directories(${LIB_NAME} INTERFACE - $<INSTALL_INTERFACE:include>) + $<INSTALL_INTERFACE:include> + $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>) install(TARGETS ${LIB_NAME} - EXPORT libcurl-target - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin + EXPORT ${TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) export(TARGETS ${LIB_NAME} @@ -143,10 +144,4 @@ export(TARGETS ${LIB_NAME} NAMESPACE CURL:: ) -install(EXPORT libcurl-target - FILE libcurl-target.cmake - NAMESPACE CURL:: - DESTINATION ${CURL_INSTALL_CMAKE_DIR} -) - endif() diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc index 76ca6d0..4aa0422 100644 --- a/Utilities/cmcurl/lib/Makefile.inc +++ b/Utilities/cmcurl/lib/Makefile.inc @@ -30,12 +30,12 @@ LIB_VAUTH_HFILES = vauth/vauth.h vauth/digest.h vauth/ntlm.h LIB_VTLS_CFILES = vtls/openssl.c vtls/gtls.c vtls/vtls.c vtls/nss.c \ vtls/polarssl.c vtls/polarssl_threadlock.c vtls/axtls.c \ vtls/cyassl.c vtls/schannel.c vtls/schannel_verify.c \ - vtls/darwinssl.c vtls/gskit.c vtls/mbedtls.c + vtls/darwinssl.c vtls/gskit.c vtls/mbedtls.c vtls/mesalink.c LIB_VTLS_HFILES = vtls/openssl.h vtls/vtls.h vtls/gtls.h \ vtls/nssg.h vtls/polarssl.h vtls/polarssl_threadlock.h vtls/axtls.h \ vtls/cyassl.h vtls/schannel.h vtls/darwinssl.h vtls/gskit.h \ - vtls/mbedtls.h + vtls/mbedtls.h vtls/mesalink.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 \ @@ -54,7 +54,8 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c \ curl_multibyte.c hostcheck.c conncache.c pipeline.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 + mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c \ + doh.c urlapi.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 \ @@ -74,7 +75,7 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \ curl_setup_once.h multihandle.h setup-vms.h pipeline.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 + curl_path.h curl_ctype.h curl_range.h psl.h doh.h urlapi-int.h LIB_RCFILES = libcurl.rc diff --git a/Utilities/cmcurl/lib/amigaos.h b/Utilities/cmcurl/lib/amigaos.h index 02bee16..7c0926c 100644 --- a/Utilities/cmcurl/lib/amigaos.h +++ b/Utilities/cmcurl/lib/amigaos.h @@ -36,4 +36,3 @@ void Curl_amiga_cleanup(); #endif #endif /* HEADER_CURL_AMIGAOS_H */ - diff --git a/Utilities/cmcurl/lib/arpa_telnet.h b/Utilities/cmcurl/lib/arpa_telnet.h index ec23872..232680e 100644 --- a/Utilities/cmcurl/lib/arpa_telnet.h +++ b/Utilities/cmcurl/lib/arpa_telnet.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -37,6 +37,7 @@ #define CURL_NEW_ENV_VAR 0 #define CURL_NEW_ENV_VALUE 1 +#ifndef CURL_DISABLE_VERBOSE_STRINGS /* * The telnet options represented as strings */ @@ -53,6 +54,7 @@ static const char * const telnetoptions[]= "TERM SPEED", "LFLOW", "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON" }; +#endif #define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON @@ -76,6 +78,7 @@ static const char * const telnetoptions[]= #define CURL_DONT 254 /* DON'T use this option! */ #define CURL_IAC 255 /* Interpret As Command */ +#ifndef CURL_DISABLE_VERBOSE_STRINGS /* * Then those numbers represented as strings: */ @@ -86,6 +89,7 @@ static const char * const telnetcmds[]= "AYT", "EC", "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC" }; +#endif #define CURL_TELCMD_MINIMUM CURL_xEOF /* the first one */ #define CURL_TELCMD_MAXIMUM CURL_IAC /* surprise, 255 is the last one! ;-) */ diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index da49c2a..a342c61 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -1218,7 +1218,6 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, { struct Cookie *newco; struct Cookie *co; - time_t now = time(NULL); struct Cookie *mainco = NULL; size_t matches = 0; bool is_ip; @@ -1236,11 +1235,8 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, co = c->cookies[myhash]; while(co) { - /* only process this cookie if it is not expired or had no expire - date AND that if the cookie requires we're secure we must only - continue if we are! */ - if((!co->expires || (co->expires > now)) && - (co->secure?secure:TRUE)) { + /* if the cookie requires we're secure we must only continue if we are! */ + if(co->secure?secure:TRUE) { /* now check if the domain is correct */ if(!co->domain || @@ -1266,12 +1262,8 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, matches++; } - else { - fail: - /* failure, clear up the allocated chain and return NULL */ - Curl_cookie_freelist(mainco); - return NULL; - } + else + goto fail; } } } @@ -1309,6 +1301,11 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, } return mainco; /* return the new list */ + +fail: + /* failure, clear up the allocated chain and return NULL */ + Curl_cookie_freelist(mainco); + return NULL; } /***************************************************************************** @@ -1508,10 +1505,9 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) format_ptr = get_netscape_format(array[i]); if(format_ptr == NULL) { fprintf(out, "#\n# Fatal libcurl error\n"); - if(!use_stdout) { - free(array); + free(array); + if(!use_stdout) fclose(out); - } return 1; } fprintf(out, "%s\n", format_ptr); diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index f67188e..d5e3a90 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake @@ -118,6 +118,9 @@ /* Define to 1 if bool is an available type. */ #cmakedefine HAVE_BOOL_T 1 +/* Define to 1 if you have the __builtin_available function. */ +#cmakedefine HAVE_BUILTIN_AVAILABLE 1 + /* Define to 1 if you have the clock_gettime function and monotonic timer. */ #cmakedefine HAVE_CLOCK_GETTIME_MONOTONIC 1 diff --git a/Utilities/cmcurl/lib/curl_ldap.h b/Utilities/cmcurl/lib/curl_ldap.h index 27d0381..94c0029 100644 --- a/Utilities/cmcurl/lib/curl_ldap.h +++ b/Utilities/cmcurl/lib/curl_ldap.h @@ -32,4 +32,3 @@ extern const struct Curl_handler Curl_handler_ldaps; #endif #endif /* HEADER_CURL_LDAP_H */ - diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c index 353a656..a4791eb 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_wb.c +++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -249,6 +249,9 @@ done: return CURLE_REMOTE_ACCESS_DENIED; } +/* if larger than this, something is seriously wrong */ +#define MAX_NTLM_WB_RESPONSE 100000 + static CURLcode ntlm_wb_response(struct connectdata *conn, const char *input, curlntlm state) { @@ -289,6 +292,13 @@ static CURLcode ntlm_wb_response(struct connectdata *conn, buf[len_out - 1] = '\0'; break; } + + if(len_out > MAX_NTLM_WB_RESPONSE) { + failf(conn->data, "too large ntlm_wb response!"); + free(buf); + return CURLE_OUT_OF_MEMORY; + } + newbuf = Curl_saferealloc(buf, len_out + NTLM_BUFSIZE); if(!newbuf) return CURLE_OUT_OF_MEMORY; @@ -314,6 +324,8 @@ static CURLcode ntlm_wb_response(struct connectdata *conn, conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3); free(buf); + if(!conn->response_header) + return CURLE_OUT_OF_MEMORY; return CURLE_OK; done: free(buf); @@ -389,6 +401,8 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, conn->response_header); DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); free(conn->response_header); + if(!*allocuserpwd) + return CURLE_OUT_OF_MEMORY; conn->response_header = NULL; break; case NTLMSTATE_TYPE2: @@ -409,6 +423,8 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */ authp->done = TRUE; Curl_ntlm_wb_cleanup(conn); + if(!*allocuserpwd) + return CURLE_OUT_OF_MEMORY; break; case NTLMSTATE_TYPE3: /* connection is already authenticated, diff --git a/Utilities/cmcurl/lib/curl_path.c b/Utilities/cmcurl/lib/curl_path.c index e843dea..68f3e44 100644 --- a/Utilities/cmcurl/lib/curl_path.c +++ b/Utilities/cmcurl/lib/curl_path.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -39,7 +39,7 @@ CURLcode Curl_getworkingpath(struct connectdata *conn, char *working_path; size_t working_path_len; CURLcode result = - Curl_urldecode(data, data->state.path, 0, &working_path, + Curl_urldecode(data, data->state.up.path, 0, &working_path, &working_path_len, FALSE); if(result) return result; diff --git a/Utilities/cmcurl/lib/curl_path.h b/Utilities/cmcurl/lib/curl_path.h index 5ee4ff3..636c37f 100644 --- a/Utilities/cmcurl/lib/curl_path.h +++ b/Utilities/cmcurl/lib/curl_path.h @@ -44,4 +44,4 @@ CURLcode Curl_getworkingpath(struct connectdata *conn, char **path); CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir); -#endif +#endif /* HEADER_CURL_PATH_H */ diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c index 9743064..f09f2f3 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.c +++ b/Utilities/cmcurl/lib/curl_rtmp.c @@ -37,9 +37,11 @@ /* The last #include file should be: */ #include "memdebug.h" -#ifdef _WIN32 +#if defined(WIN32) && !defined(USE_LWIPSOCK) #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e) #define SET_RCVTIMEO(tv,s) int tv = s*1000 +#elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD) +#define SET_RCVTIMEO(tv,s) int tv = s*1000 #else #define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0} #endif diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index b056126..5cdbc592 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -44,6 +44,9 @@ # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif +# ifndef NOGDI +# define NOGDI +# endif #endif /* @@ -660,7 +663,7 @@ int netware_init(void); #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \ defined(USE_POLARSSL) || defined(USE_AXTLS) || defined(USE_MBEDTLS) || \ defined(USE_CYASSL) || defined(USE_SCHANNEL) || \ - defined(USE_DARWINSSL) || defined(USE_GSKIT) + defined(USE_DARWINSSL) || defined(USE_GSKIT) || defined(USE_MESALINK) #define USE_SSL /* SSL support has been enabled */ #endif diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h index 6d01ea1..413ccea 100644 --- a/Utilities/cmcurl/lib/curl_setup_once.h +++ b/Utilities/cmcurl/lib/curl_setup_once.h @@ -515,4 +515,3 @@ typedef int sig_atomic_t; #endif /* HEADER_CURL_SETUP_ONCE_H */ - diff --git a/Utilities/cmcurl/lib/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c index 11a7120..1d0de4e 100644 --- a/Utilities/cmcurl/lib/curl_sspi.c +++ b/Utilities/cmcurl/lib/curl_sspi.c @@ -90,8 +90,9 @@ CURLcode Curl_sspi_global_init(void) return CURLE_FAILED_INIT; /* Get address of the InitSecurityInterfaceA function from the SSPI dll */ - pInitSecurityInterface = (INITSECURITYINTERFACE_FN) - GetProcAddress(s_hSecDll, SECURITYENTRYPOINT); + pInitSecurityInterface = + CURLX_FUNCTION_CAST(INITSECURITYINTERFACE_FN, + (GetProcAddress(s_hSecDll, SECURITYENTRYPOINT))); if(!pInitSecurityInterface) return CURLE_FAILED_INIT; @@ -131,7 +132,7 @@ void Curl_sspi_global_cleanup(void) * Parameters: * * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * identity [in/out] - The identity structure. * * Returns CURLE_OK on success. diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c index b8f0cd3..8e5937a 100644 --- a/Utilities/cmcurl/lib/curl_threads.c +++ b/Utilities/cmcurl/lib/curl_threads.c @@ -104,13 +104,21 @@ int Curl_thread_join(curl_thread_t *hnd) curl_thread_t Curl_thread_create(unsigned int (CURL_STDCALL *func) (void *), void *arg) { +#ifdef _WIN32_WCE + typedef HANDLE curl_win_thread_handle_t; +#elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) + typedef unsigned long curl_win_thread_handle_t; +#else + typedef uintptr_t curl_win_thread_handle_t; +#endif curl_thread_t t; + curl_win_thread_handle_t thread_handle; #ifdef _WIN32_WCE - t = CreateThread(NULL, 0, func, arg, 0, NULL); + thread_handle = CreateThread(NULL, 0, func, arg, 0, NULL); #else - uintptr_t thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL); - t = (curl_thread_t)thread_handle; + thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL); #endif + t = (curl_thread_t)thread_handle; if((t == 0) || (t == LongToHandle(-1L))) { #ifdef _WIN32_WCE DWORD gle = GetLastError(); diff --git a/Utilities/cmcurl/lib/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h index 9e0d14a..2a93644 100644 --- a/Utilities/cmcurl/lib/curl_threads.h +++ b/Utilities/cmcurl/lib/curl_threads.h @@ -38,7 +38,8 @@ # define curl_thread_t HANDLE # define curl_thread_t_null (HANDLE)0 # if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ - (_WIN32_WINNT < _WIN32_WINNT_VISTA) + (_WIN32_WINNT < _WIN32_WINNT_VISTA) || \ + (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) # define Curl_mutex_init(m) InitializeCriticalSection(m) # else # define Curl_mutex_init(m) InitializeCriticalSectionEx(m, 0, 1) diff --git a/Utilities/cmcurl/lib/curlx.h b/Utilities/cmcurl/lib/curlx.h index 6e41826..4c77d4f 100644 --- a/Utilities/cmcurl/lib/curlx.h +++ b/Utilities/cmcurl/lib/curlx.h @@ -102,4 +102,3 @@ #endif /* ENABLE_CURLX_PRINTF */ #endif /* HEADER_CURL_CURLX_H */ - diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c index 408d57b..78ef046 100644 --- a/Utilities/cmcurl/lib/dict.c +++ b/Utilities/cmcurl/lib/dict.c @@ -136,7 +136,7 @@ static CURLcode dict_do(struct connectdata *conn, bool *done) struct Curl_easy *data = conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - char *path = data->state.path; + char *path = data->state.up.path; curl_off_t *bytecount = &data->req.bytecount; *done = TRUE; /* unconditionally */ diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c new file mode 100644 index 0000000..ef6013d --- /dev/null +++ b/Utilities/cmcurl/lib/doh.c @@ -0,0 +1,920 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.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 "urldata.h" +#include "curl_addrinfo.h" +#include "doh.h" + +#ifdef USE_NGHTTP2 +#include "sendf.h" +#include "multiif.h" +#include "url.h" +#include "share.h" +#include "curl_base64.h" +#include "connect.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define DNS_CLASS_IN 0x01 +#define DOH_MAX_RESPONSE_SIZE 3000 /* bytes */ + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static const char * const errors[]={ + "", + "Bad label", + "Out of range", + "Label loop", + "Too small", + "Out of memory", + "RDATA length", + "Malformat", + "Bad RCODE", + "Unexpected TYPE", + "Unexpected CLASS", + "No content", + "Bad ID" +}; + +static const char *doh_strerror(DOHcode code) +{ + if((code >= DOH_OK) && (code <= DOH_DNS_BAD_ID)) + return errors[code]; + return "bad error code"; +} +#endif + +#ifdef DEBUGBUILD +#define UNITTEST +#else +#define UNITTEST static +#endif + +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); + unsigned char *orig = dnsp; + const char *hostp = host; + + if(len < (12 + hostlen + 4)) + return DOH_TOO_SMALL_BUFFER; + + *dnsp++ = 0; /* 16 bit id */ + *dnsp++ = 0; + *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */ + *dnsp++ = '\0'; /* |RA| Z | RCODE | */ + *dnsp++ = '\0'; + *dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */ + *dnsp++ = '\0'; + *dnsp++ = '\0'; /* ANCOUNT */ + *dnsp++ = '\0'; + *dnsp++ = '\0'; /* NSCOUNT */ + *dnsp++ = '\0'; + *dnsp++ = '\0'; /* ARCOUNT */ + + /* store a QNAME */ + do { + char *dot = strchr(hostp, '.'); + size_t labellen; + bool found = false; + if(dot) { + found = true; + labellen = dot - hostp; + } + else + labellen = strlen(hostp); + if(labellen > 63) { + /* too long label, error out */ + *olen = 0; + return DOH_DNS_BAD_LABEL; + } + *dnsp++ = (unsigned char)labellen; + memcpy(dnsp, hostp, labellen); + dnsp += labellen; + hostp += labellen + 1; + if(!found) { + *dnsp++ = 0; /* terminating zero */ + break; + } + } while(1); + + *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; + return DOH_OK; +} + +static size_t +doh_write_cb(void *contents, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + struct dohresponse *mem = (struct dohresponse *)userp; + + if((mem->size + realsize) > DOH_MAX_RESPONSE_SIZE) + /* suspiciously much for us */ + return 0; + + mem->memory = realloc(mem->memory, mem->size + realsize); + if(mem->memory == NULL) + /* out of memory! */ + return 0; + + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + + return realsize; +} + +/* called from multi.c when this DOH transfer is complete */ +static int Curl_doh_done(struct Curl_easy *doh, CURLcode result) +{ + struct Curl_easy *data = doh->set.dohfor; + /* so one of the DOH request done for the 'data' transfer is now complete! */ + data->req.doh.pending--; + infof(data, "a DOH request is completed, %d to go\n", data->req.doh.pending); + if(result) + infof(data, "DOH request %s\n", curl_easy_strerror(result)); + + if(!data->req.doh.pending) { + /* DOH completed */ + curl_slist_free_all(data->req.doh.headers); + data->req.doh.headers = NULL; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + return 0; +} + +#define ERROR_CHECK_SETOPT(x,y) result = curl_easy_setopt(doh, x, y); \ + if(result) goto error + +static CURLcode dohprobe(struct Curl_easy *data, + struct dnsprobe *p, DNStype dnstype, + const char *host, + const char *url, CURLM *multi, + struct curl_slist *headers) +{ + struct Curl_easy *doh = NULL; + char *nurl = NULL; + CURLcode result = CURLE_OK; + timediff_t timeout_ms; + DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer), + &p->dohlen); + if(d) { + failf(data, "Failed to encode DOH packet [%d]\n", d); + return CURLE_OUT_OF_MEMORY; + } + + 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; + + /* 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 + yank out the GET code. Discuss! */ + if(data->set.doh_get) { + char *b64; + size_t b64len; + result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen, + &b64, &b64len); + if(result) + goto error; + nurl = aprintf("%s?dns=%s", url, b64); + free(b64); + if(!nurl) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + url = nurl; + } + + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + /* 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; + ERROR_CHECK_SETOPT(CURLOPT_URL, url); + ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); + ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp); + if(!data->set.doh_get) { + ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer); + ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen); + } + ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); + ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); +#ifndef CURLDEBUG + /* enforce HTTPS if not debug */ + ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); +#endif + ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); + ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); + doh->set.fmultidone = Curl_doh_done; + doh->set.dohfor = data; /* identify for which transfer this is done */ + p->easy = doh; + + /* add this transfer to the multi handle */ + if(curl_multi_add_handle(multi, doh)) + goto error; + } + else + goto error; + free(nurl); + return CURLE_OK; + + error: + free(nurl); + Curl_close(doh); + return result; +} + +/* + * Curl_doh() resolves a name using DOH. It resolves a name 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_easy *data = conn->data; + CURLcode result = CURLE_OK; + *waitp = TRUE; /* this never returns synchronously */ + (void)conn; + (void)hostname; + (void)port; + + /* start clean, consider allocating this struct on demand */ + memset(&data->req.doh, 0, sizeof(struct dohdata)); + + data->req.doh.host = hostname; + data->req.doh.port = port; + data->req.doh.headers = + curl_slist_append(NULL, + "Content-Type: application/dns-message"); + if(!data->req.doh.headers) + goto error; + + 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], + data->multi, data->req.doh.headers); + if(result) + goto error; + data->req.doh.pending++; + } + + 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], + data->multi, data->req.doh.headers); + if(result) + goto error; + data->req.doh.pending++; + } + return NULL; + + 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; + return NULL; +} + +static DOHcode skipqname(unsigned char *doh, size_t dohlen, + unsigned int *indexp) +{ + unsigned char length; + do { + if(dohlen < (*indexp + 1)) + return DOH_DNS_OUT_OF_RANGE; + length = doh[*indexp]; + if((length & 0xc0) == 0xc0) { + /* name pointer, advance over it and be done */ + if(dohlen < (*indexp + 2)) + return DOH_DNS_OUT_OF_RANGE; + *indexp += 2; + break; + } + if(length & 0xc0) + return DOH_DNS_BAD_LABEL; + if(dohlen < (*indexp + 1 + length)) + return DOH_DNS_OUT_OF_RANGE; + *indexp += 1 + length; + } while(length); + return DOH_OK; +} + +static unsigned short get16bit(unsigned char *doh, int index) +{ + return (unsigned short)((doh[index] << 8) | doh[index + 1]); +} + +static unsigned int get32bit(unsigned char *doh, int index) +{ + return (doh[index] << 24) | (doh[index + 1] << 16) | + (doh[index + 2] << 8) | doh[index + 3]; +} + +static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d) +{ + /* silently ignore addresses over the limit */ + if(d->numaddr < DOH_MAX_ADDR) { + struct dohaddr *a = &d->addr[d->numaddr]; + a->type = DNS_TYPE_A; + memcpy(&a->ip.v4, &doh[index], 4); + d->numaddr++; + } + return DOH_OK; +} + +static DOHcode store_aaaa(unsigned char *doh, int index, struct dohentry *d) +{ + /* silently ignore addresses over the limit */ + if(d->numaddr < DOH_MAX_ADDR) { + struct dohaddr *a = &d->addr[d->numaddr]; + a->type = DNS_TYPE_AAAA; + memcpy(&a->ip.v6, &doh[index], 16); + d->numaddr++; + } + 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, + size_t dohlen, + unsigned int index, + struct dohentry *d) +{ + struct cnamestore *c; + unsigned int loop = 128; /* a valid DNS name can never loop this much */ + unsigned char length; + + if(d->numcname == DOH_MAX_CNAME) + return DOH_OK; /* skip! */ + + c = &d->cname[d->numcname++]; + do { + if(index >= dohlen) + return DOH_DNS_OUT_OF_RANGE; + length = doh[index]; + if((length & 0xc0) == 0xc0) { + int newpos; + /* name pointer, get the new offset (14 bits) */ + if((index + 1) >= dohlen) + return DOH_DNS_OUT_OF_RANGE; + + /* move to the the new index */ + newpos = (length & 0x3f) << 8 | doh[index + 1]; + index = newpos; + continue; + } + else if(length & 0xc0) + return DOH_DNS_BAD_LABEL; /* bad input */ + else + index++; + + if(length) { + DOHcode rc; + if(c->len) { + rc = cnameappend(c, (unsigned char *)".", 1); + if(rc) + return rc; + } + if((index + length) > dohlen) + return DOH_DNS_BAD_LABEL; + + rc = cnameappend(c, &doh[index], length); + if(rc) + return rc; + index += length; + } + } while(length && --loop); + + if(!loop) + return DOH_DNS_LABEL_LOOP; + return DOH_OK; +} + +static DOHcode rdata(unsigned char *doh, + size_t dohlen, + unsigned short rdlength, + unsigned short type, + int index, + struct dohentry *d) +{ + /* RDATA + - A (TYPE 1): 4 bytes + - AAAA (TYPE 28): 16 bytes + - NS (TYPE 2): N bytes */ + DOHcode rc; + + switch(type) { + case DNS_TYPE_A: + if(rdlength != 4) + return DOH_DNS_RDATA_LEN; + rc = store_a(doh, index, d); + if(rc) + return rc; + break; + case DNS_TYPE_AAAA: + if(rdlength != 16) + return DOH_DNS_RDATA_LEN; + rc = store_aaaa(doh, index, d); + if(rc) + return rc; + break; + case DNS_TYPE_CNAME: + rc = store_cname(doh, dohlen, index, d); + if(rc) + return rc; + break; + default: + /* unsupported type, just skip it */ + break; + } + return DOH_OK; +} + +static void init_dohentry(struct dohentry *de) +{ + memset(de, 0, sizeof(*de)); + de->ttl = INT_MAX; +} + + +UNITTEST DOHcode doh_decode(unsigned char *doh, + size_t dohlen, + DNStype dnstype, + struct dohentry *d) +{ + unsigned char rcode; + unsigned short qdcount; + unsigned short ancount; + unsigned short type = 0; + unsigned short class; + unsigned short rdlength; + unsigned short nscount; + unsigned short arcount; + unsigned int index = 12; + DOHcode rc; + + if(dohlen < 12) + return DOH_TOO_SMALL_BUFFER; /* too small */ + if(doh[0] || doh[1]) + return DOH_DNS_BAD_ID; /* bad ID */ + rcode = doh[3] & 0x0f; + if(rcode) + return DOH_DNS_BAD_RCODE; /* bad rcode */ + + qdcount = get16bit(doh, 4); + while(qdcount) { + rc = skipqname(doh, dohlen, &index); + if(rc) + return rc; /* bad qname */ + if(dohlen < (index + 4)) + return DOH_DNS_OUT_OF_RANGE; + index += 4; /* skip question's type and class */ + qdcount--; + } + + ancount = get16bit(doh, 6); + while(ancount) { + unsigned int ttl; + + rc = skipqname(doh, dohlen, &index); + if(rc) + return rc; /* bad qname */ + + if(dohlen < (index + 2)) + 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 */ + return DOH_DNS_UNEXPECTED_TYPE; + index += 2; + + if(dohlen < (index + 2)) + return DOH_DNS_OUT_OF_RANGE; + class = get16bit(doh, index); + if(DNS_CLASS_IN != class) + return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */ + index += 2; + + if(dohlen < (index + 4)) + return DOH_DNS_OUT_OF_RANGE; + + ttl = get32bit(doh, index); + if(ttl < d->ttl) + d->ttl = ttl; + index += 4; + + if(dohlen < (index + 2)) + return DOH_DNS_OUT_OF_RANGE; + + rdlength = get16bit(doh, index); + index += 2; + if(dohlen < (index + rdlength)) + return DOH_DNS_OUT_OF_RANGE; + + rc = rdata(doh, dohlen, rdlength, type, index, d); + if(rc) + return rc; /* bad rdata */ + index += rdlength; + ancount--; + } + + nscount = get16bit(doh, 8); + while(nscount) { + rc = skipqname(doh, dohlen, &index); + if(rc) + return rc; /* bad qname */ + + if(dohlen < (index + 8)) + return DOH_DNS_OUT_OF_RANGE; + + index += 2 + 2 + 4; /* type, class and ttl */ + + if(dohlen < (index + 2)) + return DOH_DNS_OUT_OF_RANGE; + + rdlength = get16bit(doh, index); + index += 2; + if(dohlen < (index + rdlength)) + return DOH_DNS_OUT_OF_RANGE; + index += rdlength; + nscount--; + } + + arcount = get16bit(doh, 10); + while(arcount) { + rc = skipqname(doh, dohlen, &index); + if(rc) + return rc; /* bad qname */ + + if(dohlen < (index + 8)) + return DOH_DNS_OUT_OF_RANGE; + + index += 2 + 2 + 4; /* type, class and ttl */ + + if(dohlen < (index + 2)) + return DOH_DNS_OUT_OF_RANGE; + + rdlength = get16bit(doh, index); + index += 2; + if(dohlen < (index + rdlength)) + return DOH_DNS_OUT_OF_RANGE; + index += rdlength; + arcount--; + } + + if(index != dohlen) + return DOH_DNS_MALFORMAT; /* something is wrong */ + + if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr) + /* nothing stored! */ + return DOH_NO_CONTENT; + + return DOH_OK; /* ok */ +} + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void showdoh(struct Curl_easy *data, + 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]; + if(a->type == DNS_TYPE_A) { + infof(data, "DOH A: %u.%u.%u.%u\n", + a->ip.v4[0], a->ip.v4[1], + a->ip.v4[2], a->ip.v4[3]); + } + else if(a->type == DNS_TYPE_AAAA) { + int j; + char buffer[128]; + char *ptr; + size_t len; + snprintf(buffer, 128, "DOH AAAA: "); + ptr = &buffer[10]; + len = 118; + for(j = 0; j < 16; j += 2) { + size_t l; + snprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j], + d->addr[i].ip.v6[j + 1]); + l = strlen(ptr); + len -= l; + ptr += l; + } + infof(data, "%s\n", buffer); + } + } + for(i = 0; i < d->numcname; i++) { + infof(data, "CNAME: %s\n", d->cname[i].alloc); + } +} +#else +#define showdoh(x,y) +#endif + +/* + * doh2ai() + * + * This function returns a pointer to the first element of a newly allocated + * Curl_addrinfo struct linked list filled with the data from a set of DOH + * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for + * a IPv6 stack, but usable also for IPv4, all hosts and environments. + * + * The memory allocated by this function *MUST* be free'd later on calling + * Curl_freeaddrinfo(). For each successful call to this function there + * must be an associated call later to Curl_freeaddrinfo(). + */ + +static Curl_addrinfo * +doh2ai(const struct dohentry *de, const char *hostname, int port) +{ + Curl_addrinfo *ai; + Curl_addrinfo *prevai = NULL; + Curl_addrinfo *firstai = NULL; + struct sockaddr_in *addr; +#ifdef ENABLE_IPV6 + struct sockaddr_in6 *addr6; +#endif + CURLcode result = CURLE_OK; + int i; + + if(!de) + /* no input == no output! */ + return NULL; + + for(i = 0; i < de->numaddr; i++) { + size_t ss_size; + CURL_SA_FAMILY_T addrtype; + if(de->addr[i].type == DNS_TYPE_AAAA) { +#ifndef ENABLE_IPV6 + /* we can't handle IPv6 addresses */ + continue; +#else + ss_size = sizeof(struct sockaddr_in6); + addrtype = AF_INET6; +#endif + } + else { + ss_size = sizeof(struct sockaddr_in); + addrtype = AF_INET; + } + + ai = calloc(1, sizeof(Curl_addrinfo)); + 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; + } + + if(!firstai) + /* store the pointer we want to return from this function */ + firstai = ai; + + if(prevai) + /* make the previous entry point to this */ + prevai->ai_next = ai; + + ai->ai_family = addrtype; + + /* we return all names as STREAM, so when using this address for TFTP + the type must be ignored and conn->socktype be used instead! */ + ai->ai_socktype = SOCK_STREAM; + + ai->ai_addrlen = (curl_socklen_t)ss_size; + + /* leave the rest of the struct filled with zero */ + + switch(ai->ai_family) { + case AF_INET: + addr = (void *)ai->ai_addr; /* storage area for this info */ + DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); + memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); + addr->sin_family = (CURL_SA_FAMILY_T)addrtype; + addr->sin_port = htons((unsigned short)port); + break; + +#ifdef ENABLE_IPV6 + case AF_INET6: + addr6 = (void *)ai->ai_addr; /* storage area for this info */ + DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); + memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); + addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype; + addr6->sin6_port = htons((unsigned short)port); + break; +#endif + } + + prevai = ai; + } + + if(result) { + Curl_freeaddrinfo(firstai); + firstai = NULL; + } + + return firstai; +} + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static const char *type2name(DNStype dnstype) +{ + return (dnstype == DNS_TYPE_A)?"A":"AAAA"; +} +#endif + +UNITTEST void de_cleanup(struct dohentry *d) +{ + int i = 0; + for(i = 0; i < d->numcname; i++) { + free(d->cname[i].alloc); + } +} + +CURLcode Curl_doh_is_resolved(struct connectdata *conn, + struct Curl_dns_entry **dnsp) +{ + 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) { + 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; + struct dohentry de; + struct Curl_dns_entry *dns; + struct Curl_addrinfo *ai; + /* 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); + } + 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, "DOG: %s type %s for %s\n", doh_strerror(rc2), + type2name(data->req.doh.probe[1].dnstype), + data->req.doh.host); + } + if(!rc || !rc2) { + infof(data, "DOH Host name: %s\n", data->req.doh.host); + showdoh(data, &de); + + ai = doh2ai(&de, data->req.doh.host, data->req.doh.port); + if(!ai) { + de_cleanup(&de); + return CURLE_OUT_OF_MEMORY; + } + + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + /* we got a response, store it in the cache */ + dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + de_cleanup(&de); + if(!dns) + /* returned failure, bail out nicely */ + Curl_freeaddrinfo(ai); + else { + conn->async.dns = dns; + *dnsp = dns; + return CURLE_OK; + } + } + de_cleanup(&de); + + return CURLE_COULDNT_RESOLVE_HOST; + } + + return CURLE_OK; +} + +#else /* !USE_NGHTTP2 */ +/* + */ +Curl_addrinfo *Curl_doh(struct connectdata *conn, + const char *hostname, + int port, + int *waitp) +{ + (void)conn; + (void)hostname; + (void)port; + (void)waitp; + return NULL; +} + +CURLcode Curl_doh_is_resolved(struct connectdata *conn, + struct Curl_dns_entry **dnsp) +{ + (void)conn; + (void)dnsp; + return CURLE_NOT_BUILT_IN; +} + +#endif /* USE_NGHTTP2 */ diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h new file mode 100644 index 0000000..83c79bc --- /dev/null +++ b/Utilities/cmcurl/lib/doh.h @@ -0,0 +1,105 @@ +#ifndef HEADER_CURL_DOH_H +#define HEADER_CURL_DOH_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.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 "urldata.h" +#include "curl_addrinfo.h" + +/* + * Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name + * and returns a 'Curl_addrinfo *' with the address information. + */ + +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); + +typedef enum { + DOH_OK, + DOH_DNS_BAD_LABEL, /* 1 */ + DOH_DNS_OUT_OF_RANGE, /* 2 */ + DOH_DNS_LABEL_LOOP, /* 3 */ + DOH_TOO_SMALL_BUFFER, /* 4 */ + DOH_OUT_OF_MEM, /* 5 */ + DOH_DNS_RDATA_LEN, /* 6 */ + DOH_DNS_MALFORMAT, /* 7 */ + DOH_DNS_BAD_RCODE, /* 8 - no such name */ + DOH_DNS_UNEXPECTED_TYPE, /* 9 */ + DOH_DNS_UNEXPECTED_CLASS, /* 10 */ + DOH_NO_CONTENT, /* 11 */ + DOH_DNS_BAD_ID /* 12 */ +} DOHcode; + +typedef enum { + DNS_TYPE_A = 1, + DNS_TYPE_NS = 2, + DNS_TYPE_CNAME = 5, + DNS_TYPE_AAAA = 28 +} 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 { + unsigned char v4[4]; /* network byte order */ + unsigned char v6[16]; + } ip; +}; + +struct dohentry { + unsigned int ttl; + int numaddr; + struct dohaddr addr[DOH_MAX_ADDR]; + int numcname; + struct cnamestore cname[DOH_MAX_CNAME]; +}; + + +#ifdef DEBUGBUILD +DOHcode doh_encode(const char *host, + DNStype dnstype, + unsigned char *dnsp, /* buffer */ + size_t len, /* buffer size */ + size_t *olen); /* output length */ +DOHcode doh_decode(unsigned char *doh, + size_t dohlen, + DNStype dnstype, + struct dohentry *d); +void de_cleanup(struct dohentry *d); +#endif +#endif /* HEADER_CURL_DOH_H */ diff --git a/Utilities/cmcurl/lib/dotdot.c b/Utilities/cmcurl/lib/dotdot.c index cbb308d..2c6177a 100644 --- a/Utilities/cmcurl/lib/dotdot.c +++ b/Utilities/cmcurl/lib/dotdot.c @@ -62,6 +62,8 @@ char *Curl_dedotdotify(const char *input) if(!out) return NULL; /* out of memory */ + *out = 0; /* zero terminates, for inputs like "./" */ + /* get a cloned copy of the input */ clone = strdup(input); if(!clone) { diff --git a/Utilities/cmcurl/lib/dotdot.h b/Utilities/cmcurl/lib/dotdot.h index fac8e6f..125af43 100644 --- a/Utilities/cmcurl/lib/dotdot.h +++ b/Utilities/cmcurl/lib/dotdot.h @@ -22,4 +22,4 @@ * ***************************************************************************/ char *Curl_dedotdotify(const char *input); -#endif +#endif /* HEADER_CURL_DOTDOT_H */ diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index 027d0be..4de4e65 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -1002,10 +1002,6 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) */ void curl_easy_reset(struct Curl_easy *data) { - Curl_safefree(data->state.pathbuffer); - - data->state.path = NULL; - Curl_free_request_state(data); /* zero out UserDefined data: */ @@ -1197,3 +1193,22 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, return result; } + +/* + * Performs connection upkeep for the given session handle. + */ +CURLcode curl_easy_upkeep(struct Curl_easy *data) +{ + /* Verify that we got an easy handle we can work with. */ + if(!GOOD_EASY_HANDLE(data)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(data->multi_easy) { + /* Use the common function to keep connections alive. */ + return Curl_upkeep(&data->multi_easy->conn_cache, data); + } + else { + /* No connections, so just return success */ + return CURLE_OK; + } +} diff --git a/Utilities/cmcurl/lib/easyif.h b/Utilities/cmcurl/lib/easyif.h index f6132cc..6ba7e54 100644 --- a/Utilities/cmcurl/lib/easyif.h +++ b/Utilities/cmcurl/lib/easyif.h @@ -30,4 +30,3 @@ CURL_EXTERN CURLcode curl_easy_perform_ev(struct Curl_easy *easy); #endif #endif /* HEADER_CURL_EASYIF_H */ - diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c index 10774f0..afd3899 100644 --- a/Utilities/cmcurl/lib/escape.c +++ b/Utilities/cmcurl/lib/escape.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -41,7 +41,7 @@ its behavior is altered by the current locale. See https://tools.ietf.org/html/rfc3986#section-2.3 */ -static bool Curl_isunreserved(unsigned char in) +bool Curl_isunreserved(unsigned char in) { switch(in) { case '0': case '1': case '2': case '3': case '4': @@ -141,6 +141,8 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, * 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. */ CURLcode Curl_urldecode(struct Curl_easy *data, const char *string, size_t length, @@ -151,7 +153,7 @@ CURLcode Curl_urldecode(struct Curl_easy *data, char *ns = malloc(alloc); size_t strindex = 0; unsigned long hex; - CURLcode result; + CURLcode result = CURLE_OK; if(!ns) return CURLE_OUT_OF_MEMORY; @@ -171,11 +173,13 @@ CURLcode Curl_urldecode(struct Curl_easy *data, in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */ - result = Curl_convert_from_network(data, (char *)&in, 1); - if(result) { - /* Curl_convert_from_network calls failf if unsuccessful */ - free(ns); - return result; + if(data) { + result = Curl_convert_from_network(data, (char *)&in, 1); + if(result) { + /* Curl_convert_from_network calls failf if unsuccessful */ + free(ns); + return result; + } } string += 2; diff --git a/Utilities/cmcurl/lib/escape.h b/Utilities/cmcurl/lib/escape.h index 638666f..d8bbe5c 100644 --- a/Utilities/cmcurl/lib/escape.h +++ b/Utilities/cmcurl/lib/escape.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,10 +24,10 @@ /* Escape and unescape URL encoding in strings. The functions return a new * allocated string or NULL if an error occurred. */ +bool Curl_isunreserved(unsigned char in); CURLcode Curl_urldecode(struct Curl_easy *data, const char *string, size_t length, char **ostring, size_t *olen, bool reject_crlf); #endif /* HEADER_CURL_ESCAPE_H */ - diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c index e50e988..722b55e 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c @@ -143,7 +143,7 @@ static CURLcode file_connect(struct connectdata *conn, bool *done) #endif size_t real_path_len; - CURLcode result = Curl_urldecode(data, data->state.path, 0, &real_path, + CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &real_path, &real_path_len, FALSE); if(result) return result; @@ -197,7 +197,7 @@ static CURLcode file_connect(struct connectdata *conn, bool *done) file->fd = fd; if(!data->set.upload && (fd == -1)) { - failf(data, "Couldn't open file %s", data->state.path); + failf(data, "Couldn't open file %s", data->state.up.path); file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE); return CURLE_FILE_COULDNT_READ_FILE; } @@ -307,7 +307,7 @@ static CURLcode file_upload(struct connectdata *conn) size_t nread; size_t nwrite; size_t readcount; - result = Curl_fillreadbuffer(conn, (int)data->set.buffer_size, &readcount); + result = Curl_fillreadbuffer(conn, data->set.buffer_size, &readcount); if(result) break; @@ -386,7 +386,6 @@ static CURLcode file_do(struct connectdata *conn, bool *done) *done = TRUE; /* unconditionally */ - Curl_initinfo(data); Curl_pgrsStartNow(data); if(data->set.upload) @@ -413,21 +412,18 @@ static CURLcode file_do(struct connectdata *conn, bool *done) } } - /* If we have selected NOBODY and HEADER, it means that we only want file - information. Which for FILE can't be much more than the file size and - date. */ - if(data->set.opt_no_body && data->set.include_header && fstated) { + if(fstated) { time_t filetime; struct tm buffer; const struct tm *tm = &buffer; char header[80]; snprintf(header, sizeof(header), "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size); - result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0); + result = Curl_client_write(conn, CLIENTWRITE_HEADER, header, 0); if(result) return result; - result = Curl_client_write(conn, CLIENTWRITE_BOTH, + result = Curl_client_write(conn, CLIENTWRITE_HEADER, (char *)"Accept-ranges: bytes\r\n", 0); if(result) return result; @@ -439,19 +435,22 @@ static CURLcode file_do(struct connectdata *conn, bool *done) /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ snprintf(header, sizeof(header), - "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", + "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s", Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], tm->tm_mday, Curl_month[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, - tm->tm_sec); - result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0); - if(!result) - /* set the file size to make it available post transfer */ - Curl_pgrsSetDownloadSize(data, expected_size); - return result; + tm->tm_sec, + data->set.opt_no_body ? "": "\r\n"); + result = Curl_client_write(conn, CLIENTWRITE_HEADER, header, 0); + if(result) + return result; + /* set the file size to make it available post transfer */ + Curl_pgrsSetDownloadSize(data, expected_size); + if(data->set.opt_no_body) + return result; } /* Check whether file range has been specified */ diff --git a/Utilities/cmcurl/lib/file.h b/Utilities/cmcurl/lib/file.h index c12ae0e..20828ad 100644 --- a/Utilities/cmcurl/lib/file.h +++ b/Utilities/cmcurl/lib/file.h @@ -38,4 +38,3 @@ extern const struct Curl_handler Curl_handler_file; #endif #endif /* HEADER_CURL_FILE_H */ - diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 63f32cf..ce889ab 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -1444,6 +1444,7 @@ static CURLcode ftp_state_list(struct connectdata *conn) { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; + struct FTP *ftp = data->req.protop; /* If this output is to be machine-parsed, the NLST command might be better to use, since the LIST command output is not specified or standard in any @@ -1460,7 +1461,7 @@ static CURLcode ftp_state_list(struct connectdata *conn) then just do LIST (in that case: nothing to do here) */ char *cmd, *lstArg, *slashPos; - const char *inpath = data->state.path; + const char *inpath = ftp->path; lstArg = NULL; if((data->set.ftp_filemethod == FTPFILE_NOCWD) && @@ -3141,7 +3142,6 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, int ftpcode; CURLcode result = CURLE_OK; char *path = NULL; - const char *path_to_use = data->state.path; if(!ftp) return CURLE_OK; @@ -3193,7 +3193,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, if(!result) /* get the "raw" path */ - result = Curl_urldecode(data, path_to_use, 0, &path, NULL, TRUE); + result = Curl_urldecode(data, ftp->path, 0, &path, NULL, TRUE); if(result) { /* We can limp along anyway (and should try to since we may already be in * the error path) */ @@ -3213,9 +3213,11 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, ftpc->prevpath[dlen] = 0; /* terminate */ } else { + free(path); /* we never changed dir */ ftpc->prevpath = strdup(""); - free(path); + if(!ftpc->prevpath) + return CURLE_OUT_OF_MEMORY; } if(ftpc->prevpath) infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath); @@ -3346,7 +3348,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, /* Send any post-transfer QUOTE strings? */ if(!status && !result && !premature && data->set.postquote) result = ftp_sendquote(conn, data->set.postquote); - + Curl_safefree(ftp->pathalloc); return result; } @@ -3696,12 +3698,13 @@ static void wc_data_dtor(void *ptr) static CURLcode init_wc_data(struct connectdata *conn) { char *last_slash; - char *path = conn->data->state.path; + struct FTP *ftp = conn->data->req.protop; + char *path = ftp->path; struct WildcardData *wildcard = &(conn->data->wildcard); CURLcode result = CURLE_OK; struct ftp_wc *ftpwc = NULL; - last_slash = strrchr(conn->data->state.path, '/'); + last_slash = strrchr(ftp->path, '/'); if(last_slash) { last_slash++; if(last_slash[0] == '\0') { @@ -3758,7 +3761,7 @@ static CURLcode init_wc_data(struct connectdata *conn) goto fail; } - wildcard->path = strdup(conn->data->state.path); + wildcard->path = strdup(ftp->path); if(!wildcard->path) { result = CURLE_OUT_OF_MEMORY; goto fail; @@ -3829,16 +3832,15 @@ static CURLcode wc_statemach(struct connectdata *conn) /* filelist has at least one file, lets get first one */ struct ftp_conn *ftpc = &conn->proto.ftpc; struct curl_fileinfo *finfo = wildcard->filelist.head->ptr; + struct FTP *ftp = conn->data->req.protop; char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename); if(!tmp_path) return CURLE_OUT_OF_MEMORY; - /* switch default "state.pathbuffer" and tmp_path, good to see - ftp_parse_url_path function to understand this trick */ - Curl_safefree(conn->data->state.pathbuffer); - conn->data->state.pathbuffer = tmp_path; - conn->data->state.path = tmp_path; + /* switch default ftp->path and tmp_path */ + free(ftp->pathalloc); + ftp->pathalloc = ftp->path = tmp_path; infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename); if(conn->data->set.chunk_bgn) { @@ -3964,10 +3966,14 @@ CURLcode Curl_ftpsend(struct connectdata *conn, const char *cmd) enum protection_level data_sec = conn->data_prot; #endif + if(!cmd) + return CURLE_BAD_FUNCTION_ARGUMENT; + write_len = strlen(cmd); - if(write_len > (sizeof(s) -3)) + if(!write_len || write_len > (sizeof(s) -3)) return CURLE_BAD_FUNCTION_ARGUMENT; + memcpy(&s, cmd, write_len); strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */ write_len += 2; bytes_written = 0; @@ -4102,7 +4108,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) 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 = data->state.path; + const char *path_to_use = ftp->path; const char *cur_pos; const char *filename = NULL; @@ -4188,7 +4194,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) /* 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 - data->state.path > 0) && + ssize_t absolute_dir = ((cur_pos - ftp->path > 0) && (ftpc->dirdepth == 0))?1:0; /* seek out the next path component */ @@ -4265,7 +4271,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) size_t dlen; char *path; CURLcode result = - Curl_urldecode(conn->data, data->state.path, 0, &path, &dlen, TRUE); + Curl_urldecode(conn->data, ftp->path, 0, &path, &dlen, TRUE); if(result) { freedirs(ftpc); return result; @@ -4385,16 +4391,16 @@ static CURLcode ftp_setup_connection(struct connectdata *conn) char *type; struct FTP *ftp; - conn->data->req.protop = ftp = malloc(sizeof(struct FTP)); + conn->data->req.protop = ftp = calloc(sizeof(struct FTP), 1); if(NULL == ftp) return CURLE_OUT_OF_MEMORY; - data->state.path++; /* don't include the initial slash */ + ftp->path = &data->state.up.path[1]; /* don't include the initial slash */ data->state.slash_removed = TRUE; /* we've skipped the slash */ /* FTP URLs support an extension like ";type=<typecode>" that * we'll try to get now! */ - type = strstr(data->state.path, ";type="); + type = strstr(ftp->path, ";type="); if(!type) type = strstr(conn->host.rawalloc, ";type="); diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h index 7ec3391..38d0322 100644 --- a/Utilities/cmcurl/lib/ftp.h +++ b/Utilities/cmcurl/lib/ftp.h @@ -105,6 +105,8 @@ struct FTP { curl_off_t *bytecountp; 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 */ /* transfer a file/body or not, done as a typedefed enum just to make debuggers display the full symbol and not just the numerical value */ diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c index 14b4562..54c2c2f 100644 --- a/Utilities/cmcurl/lib/getinfo.c +++ b/Utilities/cmcurl/lib/getinfo.c @@ -85,7 +85,6 @@ CURLcode Curl_initinfo(struct Curl_easy *data) #ifdef USE_SSL Curl_ssl_free_certinfo(data); #endif - return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c index 3ecee9b..b441a64 100644 --- a/Utilities/cmcurl/lib/gopher.c +++ b/Utilities/cmcurl/lib/gopher.c @@ -78,7 +78,7 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; curl_off_t *bytecount = &data->req.bytecount; - char *path = data->state.path; + char *path = data->state.up.path; char *sel = NULL; char *sel_org = NULL; ssize_t amount, k; diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c index e7b399e..6ff60ba 100644 --- a/Utilities/cmcurl/lib/hostasyn.c +++ b/Utilities/cmcurl/lib/hostasyn.c @@ -111,31 +111,6 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn, return result; } -/* Call this function after Curl_connect() has returned async=TRUE and - then a successful name resolve has been received. - - Note: this function disconnects and frees the conn data in case of - resolve failure */ -CURLcode Curl_async_resolved(struct connectdata *conn, - bool *protocol_done) -{ - CURLcode result; - - if(conn->async.dns) { - conn->dns_entry = conn->async.dns; - conn->async.dns = NULL; - } - - 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 */ - - return result; -} - /* * Curl_getaddrinfo() is the generic low-level name resolve API within this * source file. There are several versions of this function - for different diff --git a/Utilities/cmcurl/lib/hostcheck.h b/Utilities/cmcurl/lib/hostcheck.h index 86e3b96..f562df9 100644 --- a/Utilities/cmcurl/lib/hostcheck.h +++ b/Utilities/cmcurl/lib/hostcheck.h @@ -29,4 +29,3 @@ int Curl_cert_hostcheck(const char *match_pattern, const char *hostname); #endif /* HEADER_CURL_HOSTCHECK_H */ - diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index bc20f71..f589a0b 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -60,6 +60,7 @@ #include "url.h" #include "inet_ntop.h" #include "multiif.h" +#include "doh.h" #include "warnless.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -454,7 +455,7 @@ Curl_cache_addr(struct Curl_easy *data, /* shuffle addresses if requested */ if(data->set.dns_shuffle_addresses) { CURLcode result = Curl_shuffle_addr(data, &addr); - if(!result) + if(result) return NULL; } @@ -565,23 +566,27 @@ int Curl_resolv(struct connectdata *conn, return CURLRESOLV_ERROR; } - /* 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, + if(data->set.doh) { + 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) { /* the response to our resolve call will come asynchronously at a later time, good or bad */ /* First, check that we haven't received the info by now */ - result = Curl_resolver_is_resolved(conn, &dns); + result = Curl_resolv_check(conn, &dns); if(result) /* error detected */ return CURLRESOLV_ERROR; if(dns) @@ -1053,3 +1058,54 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) return CURLE_OK; } + +CURLcode Curl_resolv_check(struct connectdata *conn, + struct Curl_dns_entry **dns) +{ + if(conn->data->set.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) +{ +#ifdef CURLRES_ASYNCH + if(conn->data->set.doh) + /* nothing to wait for during DOH resolve, those handles have their own + sockets */ + return GETSOCK_BLANK; + return Curl_resolver_getsock(conn, socks, numsocks); +#else + (void)conn; + (void)socks; + (void)numsocks; + return GETSOCK_BLANK; +#endif +} + +/* Call this function after Curl_connect() has returned async=TRUE and + then a successful name resolve has been received. + + Note: this function disconnects and frees the conn data in case of + resolve failure */ +CURLcode Curl_once_resolved(struct connectdata *conn, + bool *protocol_done) +{ + CURLcode result; + + if(conn->async.dns) { + conn->dns_entry = conn->async.dns; + conn->async.dns = NULL; + } + + 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 */ + + return result; +} diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index 1de4bee..29fd1ef 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h @@ -145,12 +145,7 @@ int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa, /* IPv4 threadsafe resolve function used for synch and asynch builds */ Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port); -CURLcode Curl_async_resolved(struct connectdata *conn, - bool *protocol_connect); - -#ifndef CURLRES_ASYNCH -#define Curl_async_resolved(x,y) CURLE_OK -#endif +CURLcode Curl_once_resolved(struct connectdata *conn, bool *protocol_connect); /* * Curl_addrinfo_callback() is used when we build with any asynch specialty. @@ -258,4 +253,10 @@ void Curl_hostcache_destroy(struct Curl_easy *data); */ 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); + #endif /* HEADER_CURL_HOSTIP_H */ diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index e727ed8..46ac15a 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -169,7 +169,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn) data->req.protop = http; if(!CONN_INUSE(conn)) - /* if not alredy multi-using, setup connection details */ + /* if not already multi-using, setup connection details */ Curl_http2_setup_conn(conn); Curl_http2_setup_req(data); return CURLE_OK; @@ -537,14 +537,6 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) } if(pickhost || pickproxy) { - /* In case this is GSS auth, the newurl field is already allocated so - we must make sure to free it before allocating a new one. As figured - out in bug #2284386 */ - Curl_safefree(data->req.newurl); - data->req.newurl = strdup(data->change.url); /* clone URL */ - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; - if((data->set.httpreq != HTTPREQ_GET) && (data->set.httpreq != HTTPREQ_HEAD) && !conn->bits.rewindaftersend) { @@ -552,6 +544,13 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) if(result) return result; } + /* In case this is GSS auth, the newurl field is already allocated so + we must make sure to free it before allocating a new one. As figured + out in bug #2284386 */ + Curl_safefree(data->req.newurl); + data->req.newurl = strdup(data->change.url); /* clone URL */ + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; } else if((data->req.httpcode < 300) && (!data->state.authhost.done) && @@ -1094,11 +1093,13 @@ Curl_send_buffer *Curl_add_buffer_init(void) /* * Curl_add_buffer_free() frees all associated resources. */ -void Curl_add_buffer_free(Curl_send_buffer *buff) +void Curl_add_buffer_free(Curl_send_buffer **inp) { - if(buff) /* deal with NULL input */ - free(buff->buffer); - free(buff); + Curl_send_buffer *in = *inp; + if(in) /* deal with NULL input */ + free(in->buffer); + free(in); + *inp = NULL; } /* @@ -1107,7 +1108,7 @@ void Curl_add_buffer_free(Curl_send_buffer *buff) * * Returns CURLcode */ -CURLcode Curl_add_buffer_send(Curl_send_buffer *in, +CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, struct connectdata *conn, /* add the number of sent bytes to this @@ -1128,6 +1129,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, size_t sendsize; curl_socket_t sockfd; size_t headersize; + Curl_send_buffer *in = *inp; DEBUGASSERT(socketindex <= SECONDARYSOCKET); @@ -1148,7 +1150,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, /* Curl_convert_to_network calls failf if unsuccessful */ if(result) { /* conversion failed, free memory and return to the caller */ - Curl_add_buffer_free(in); + Curl_add_buffer_free(inp); return result; } @@ -1172,7 +1174,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, result = Curl_get_upload_buffer(data); if(result) { /* malloc failed, free memory and return to the caller */ - Curl_add_buffer_free(in); + Curl_add_buffer_free(&in); return result; } memcpy(data->state.ulbuf, ptr, sendsize); @@ -1256,7 +1258,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, Curl_pipeline_leave_write(conn); } } - Curl_add_buffer_free(in); + Curl_add_buffer_free(&in); return result; } @@ -1265,31 +1267,35 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, /* * add_bufferf() add the formatted input to the buffer. */ -CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...) +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(in, s, strlen(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; } /* - * add_buffer() appends a memory chunk to the existing buffer + * Curl_add_buffer() appends a memory chunk to the existing buffer */ -CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size) +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 @@ -1297,6 +1303,7 @@ CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size) size will fit into a single allocatable memory chunk */ Curl_safefree(in->buffer); free(in); + *inp = NULL; return CURLE_OUT_OF_MEMORY; } @@ -1323,6 +1330,7 @@ CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size) if(!new_rb) { /* If we failed, we cleanup the whole buffer and return error */ free(in); + *inp = NULL; return CURLE_OUT_OF_MEMORY; } @@ -1484,11 +1492,11 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn) if(!req_buffer) return CURLE_OUT_OF_MEMORY; - result = Curl_add_bufferf(req_buffer, proxy_header); + result = Curl_add_bufferf(&req_buffer, proxy_header); if(result) return result; - result = Curl_add_buffer_send(req_buffer, + result = Curl_add_buffer_send(&req_buffer, conn, &conn->data->info.request_size, 0, @@ -1561,8 +1569,7 @@ CURLcode Curl_http_done(struct connectdata *conn, return CURLE_OK; if(http->send_buffer) { - Curl_add_buffer_free(http->send_buffer); - http->send_buffer = NULL; /* clear the pointer */ + Curl_add_buffer_free(&http->send_buffer); } Curl_http2_done(conn, premature); @@ -1653,8 +1660,8 @@ 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_add_bufferf(&req_buffer, + "Expect: 100-continue\r\n"); if(!result) data->state.expect100header = TRUE; } @@ -1785,7 +1792,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", headers->data); + result = Curl_add_bufferf(&req_buffer, "%s\r\n", headers->data); } if(semicolonp) *semicolonp = ';'; /* put back the semicolon */ @@ -1854,7 +1861,7 @@ 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_add_buffer(&req_buffer, datestr, strlen(datestr)); return result; } @@ -1869,7 +1876,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) struct Curl_easy *data = conn->data; CURLcode result = CURLE_OK; struct HTTP *http; - const char *ppath = data->state.path; + const char *path = data->state.up.path; + const char *query = data->state.up.query; bool paste_ftp_userpwd = FALSE; char ftp_typecode[sizeof("/;type=?")] = ""; const char *host = conn->host.name; @@ -1987,7 +1995,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } /* setup the authentication headers */ - result = Curl_http_output_auth(conn, request, ppath, FALSE); + result = Curl_http_output_auth(conn, request, path, FALSE); if(result) return result; @@ -2215,47 +2223,59 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* The path sent to the proxy is in fact the entire URL. But if the remote host is a IDN-name, we must make sure that the request we produce only uses the encoded host name! */ + + /* and no fragment part */ + CURLUcode uc; + char *url; + CURLU *h = curl_url_dup(data->state.uh); + if(!h) + return CURLE_OUT_OF_MEMORY; + if(conn->host.dispname != conn->host.name) { - char *url = data->change.url; - ptr = strstr(url, conn->host.dispname); - if(ptr) { - /* This is where the display name starts in the URL, now replace this - part with the encoded name. TODO: This method of replacing the host - name is rather crude as I believe there's a slight risk that the - user has entered a user name or password that contain the host name - string. */ - size_t currlen = strlen(conn->host.dispname); - size_t newlen = strlen(conn->host.name); - size_t urllen = strlen(url); - - char *newurl; - - newurl = malloc(urllen + newlen - currlen + 1); - if(newurl) { - /* copy the part before the host name */ - memcpy(newurl, url, ptr - url); - /* append the new host name instead of the old */ - memcpy(newurl + (ptr - url), conn->host.name, newlen); - /* append the piece after the host name */ - memcpy(newurl + newlen + (ptr - url), - ptr + currlen, /* copy the trailing zero byte too */ - urllen - (ptr-url) - currlen + 1); - if(data->change.url_alloc) { - Curl_safefree(data->change.url); - data->change.url_alloc = FALSE; - } - data->change.url = newurl; - data->change.url_alloc = TRUE; - } - else - return CURLE_OUT_OF_MEMORY; + uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); + if(uc) { + curl_url_cleanup(h); + return CURLE_OUT_OF_MEMORY; + } + } + uc = curl_url_set(h, CURLUPART_FRAGMENT, NULL, 0); + if(uc) { + curl_url_cleanup(h); + return CURLE_OUT_OF_MEMORY; + } + + if(strcasecompare("http", data->state.up.scheme)) { + /* when getting HTTP, we don't want the userinfo the URL */ + uc = curl_url_set(h, CURLUPART_USER, NULL, 0); + if(uc) { + curl_url_cleanup(h); + return CURLE_OUT_OF_MEMORY; + } + uc = curl_url_set(h, CURLUPART_PASSWORD, NULL, 0); + if(uc) { + curl_url_cleanup(h); + return CURLE_OUT_OF_MEMORY; } } - ppath = data->change.url; - if(checkprefix("ftp://", ppath)) { + /* now extract the new version of the URL */ + uc = curl_url_get(h, CURLUPART_URL, &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)) { if(data->set.proxy_transfer_mode) { /* when doing ftp, append ;type=<a|i> if not present */ - char *type = strstr(ppath, ";type="); + char *type = strstr(path, ";type="); if(type && type[6] && type[7] == 0) { switch(Curl_raw_toupper(type[6])) { case 'A': @@ -2270,7 +2290,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) char *p = ftp_typecode; /* avoid sending invalid URLs like ftp://example.com;type=i if the * user specified ftp://example.com without the slash */ - if(!*data->state.path && ppath[strlen(ppath) - 1] != '/') { + if(!*data->state.up.path && path[strlen(path) - 1] != '/') { *p++ = '/'; } snprintf(p, sizeof(ftp_typecode) - 1, ";type=%c", @@ -2419,25 +2439,36 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* add the main request stuff */ /* GET/HEAD/POST/PUT */ - result = Curl_add_bufferf(req_buffer, "%s ", request); + result = Curl_add_bufferf(&req_buffer, "%s ", request); if(result) return result; - if(data->set.str[STRING_TARGET]) - ppath = data->set.str[STRING_TARGET]; + if(data->set.str[STRING_TARGET]) { + path = data->set.str[STRING_TARGET]; + query = NULL; + } /* url */ - if(paste_ftp_userpwd) - result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s", + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + char *url = data->change.url; + result = Curl_add_buffer(&req_buffer, url, strlen(url)); + } + else if(paste_ftp_userpwd) + result = Curl_add_bufferf(&req_buffer, "ftp://%s:%s@%s", conn->user, conn->passwd, - ppath + sizeof("ftp://") - 1); - else - result = Curl_add_buffer(req_buffer, ppath, strlen(ppath)); + path + sizeof("ftp://") - 1); + else { + result = Curl_add_buffer(&req_buffer, path, strlen(path)); + if(result) + return result; + if(query) + result = Curl_add_bufferf(&req_buffer, "?%s", query); + } if(result) return result; result = - Curl_add_bufferf(req_buffer, + Curl_add_bufferf(&req_buffer, "%s" /* ftp typecode (;type=x) */ " HTTP/%s\r\n" /* HTTP version */ "%s" /* host */ @@ -2507,7 +2538,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) co = Curl_cookie_getlist(data->cookies, conn->allocptr.cookiehost? conn->allocptr.cookiehost:host, - data->state.path, + data->state.up.path, (conn->handler->protocol&CURLPROTO_HTTPS)? TRUE:FALSE); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); @@ -2518,11 +2549,11 @@ 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_add_bufferf(&req_buffer, "Cookie: "); if(result) break; } - result = Curl_add_bufferf(req_buffer, + result = Curl_add_bufferf(&req_buffer, "%s%s=%s", count?"; ":"", co->name, co->value); if(result) @@ -2535,15 +2566,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } if(addcookies && !result) { if(!count) - result = Curl_add_bufferf(req_buffer, "Cookie: "); + result = Curl_add_bufferf(&req_buffer, "Cookie: "); if(!result) { - result = Curl_add_bufferf(req_buffer, "%s%s", count?"; ":"", + result = Curl_add_bufferf(&req_buffer, "%s%s", count?"; ":"", addcookies); count++; } } if(count && !result) - result = Curl_add_buffer(req_buffer, "\r\n", 2); + result = Curl_add_buffer(&req_buffer, "\r\n", 2); if(result) return result; @@ -2577,7 +2608,7 @@ 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, + result = Curl_add_bufferf(&req_buffer, "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", postsize); if(result) @@ -2590,7 +2621,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return result; } - result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */ + result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers */ if(result) return result; @@ -2598,7 +2629,7 @@ 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, + result = Curl_add_buffer_send(&req_buffer, conn, &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending PUT request"); @@ -2616,11 +2647,11 @@ 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_add_bufferf(&req_buffer, "Content-Length: 0\r\n\r\n"); if(result) return result; - result = Curl_add_buffer_send(req_buffer, conn, + result = Curl_add_buffer_send(&req_buffer, conn, &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending POST request"); @@ -2640,7 +2671,7 @@ 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, + result = Curl_add_bufferf(&req_buffer, "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", postsize); if(result) @@ -2652,7 +2683,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_add_bufferf(&req_buffer, "%s\r\n", hdr->data); if(result) return result; } @@ -2676,7 +2707,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_add_buffer(&req_buffer, "\r\n", 2); if(result) return result; @@ -2689,7 +2720,7 @@ 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, + result = Curl_add_buffer_send(&req_buffer, conn, &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending POST request"); @@ -2719,7 +2750,7 @@ 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, + result = Curl_add_bufferf(&req_buffer, "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", postsize); if(result) @@ -2727,7 +2758,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } if(!Curl_checkheaders(conn, "Content-Type")) { - result = Curl_add_bufferf(req_buffer, + result = Curl_add_bufferf(&req_buffer, "Content-Type: application/" "x-www-form-urlencoded\r\n"); if(result) @@ -2765,31 +2796,31 @@ 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! */ + result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers! */ 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, + result = Curl_add_buffer(&req_buffer, 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_add_bufferf(&req_buffer, "%x\r\n", (int)postsize); if(!result) { - result = Curl_add_buffer(req_buffer, data->set.postfields, + result = Curl_add_buffer(&req_buffer, data->set.postfields, (size_t)postsize); if(!result) - result = Curl_add_buffer(req_buffer, "\r\n", 2); + result = Curl_add_buffer(&req_buffer, "\r\n", 2); included_body = postsize + 2; } } if(!result) - result = Curl_add_buffer(req_buffer, "\x30\x0d\x0a\x0d\x0a", 5); + result = Curl_add_buffer(&req_buffer, "\x30\x0d\x0a\x0d\x0a", 5); /* 0 CR LF CR LF */ included_body += 5; } @@ -2811,20 +2842,20 @@ 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! */ + result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers! */ if(result) return result; } } else { - result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */ + result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers! */ 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, + result = Curl_add_buffer(&req_buffer, "\x30\x0d\x0a\x0d\x0a", 5); /* 0 CR LF CR LF */ if(result) @@ -2845,7 +2876,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } } /* issue the request */ - result = Curl_add_buffer_send(req_buffer, conn, &data->info.request_size, + result = Curl_add_buffer_send(&req_buffer, conn, &data->info.request_size, (size_t)included_body, FIRSTSOCKET); if(result) @@ -2857,12 +2888,12 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) break; default: - result = Curl_add_buffer(req_buffer, "\r\n", 2); + result = Curl_add_buffer(&req_buffer, "\r\n", 2); if(result) return result; /* issue the request */ - result = Curl_add_buffer_send(req_buffer, conn, + result = Curl_add_buffer_send(&req_buffer, conn, &data->info.request_size, 0, FIRSTSOCKET); if(result) @@ -3828,7 +3859,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, here, or else use real peer host name. */ conn->allocptr.cookiehost? conn->allocptr.cookiehost:conn->host.name, - data->state.path); + data->state.up.path); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } #endif diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index 1d373e8..21fa701 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h @@ -58,10 +58,12 @@ struct Curl_send_buffer { typedef struct Curl_send_buffer Curl_send_buffer; Curl_send_buffer *Curl_add_buffer_init(void); -void Curl_add_buffer_free(Curl_send_buffer *buff); -CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...); -CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size); -CURLcode Curl_add_buffer_send(Curl_send_buffer *in, +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, long *bytes_written, size_t included_body_bytes, @@ -154,9 +156,11 @@ struct HTTP { HTTPSEND_LAST /* never use this */ } sending; - void *send_buffer; /* used if the request couldn't be sent in one chunk, - points to an allocated send_buffer struct */ - +#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 */ +#endif #ifdef USE_NGHTTP2 /*********** for HTTP/2 we store stream-local data here *************/ int32_t stream_id; /* stream we are interested in */ @@ -253,4 +257,3 @@ Curl_http_output_auth(struct connectdata *conn, up the proxy tunnel */ #endif /* HEADER_CURL_HTTP_H */ - diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index d769193..0c5f6db 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -141,10 +141,8 @@ static int http2_getsock(struct connectdata *conn, static void http2_stream_free(struct HTTP *http) { if(http) { - Curl_add_buffer_free(http->header_recvbuf); - http->header_recvbuf = NULL; /* clear the pointer */ - Curl_add_buffer_free(http->trailer_recvbuf); - http->trailer_recvbuf = NULL; /* clear the pointer */ + Curl_add_buffer_free(&http->header_recvbuf); + Curl_add_buffer_free(&http->trailer_recvbuf); for(; http->push_headers_used > 0; --http->push_headers_used) { free(http->push_headers[http->push_headers_used - 1]); } @@ -203,7 +201,7 @@ static bool http2_connisdead(struct connectdata *conn) dead = !Curl_connalive(conn); if(!dead) { /* This happens before we've sent off a request and the connection is - not in use by any other thransfer, there shouldn't be any data here, + not in use by any other transfer, there shouldn't be any data here, only "protocol frames" */ CURLcode result; struct http_conn *httpc = &conn->proto.httpc; @@ -233,12 +231,43 @@ static unsigned int http2_conncheck(struct connectdata *check, unsigned int checks_to_perform) { unsigned int ret_val = CONNRESULT_NONE; + struct http_conn *c = &check->proto.httpc; + int rc; + bool send_frames = false; if(checks_to_perform & CONNCHECK_ISDEAD) { if(http2_connisdead(check)) ret_val |= CONNRESULT_DEAD; } + if(checks_to_perform & CONNCHECK_KEEPALIVE) { + struct curltime now = Curl_now(); + time_t elapsed = Curl_timediff(now, check->keepalive); + + if(elapsed > check->upkeep_interval_ms) { + /* Perform an HTTP/2 PING */ + rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL); + if(!rc) { + /* Successfully added a PING frame to the session. Need to flag this + so the frame is sent. */ + send_frames = true; + } + else { + failf(check->data, "nghttp2_submit_ping() failed: %s(%d)", + nghttp2_strerror(rc), rc); + } + + check->keepalive = now; + } + } + + if(send_frames) { + rc = nghttp2_session_send(c->h2); + if(rc) + failf(check->data, "nghttp2_session_send() failed: %s(%d)", + nghttp2_strerror(rc), rc); + } + return ret_val; } @@ -599,6 +628,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, int rv; size_t left, ncopy; int32_t stream_id = frame->hd.stream_id; + CURLcode result; if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ @@ -674,7 +704,9 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, stream->status_code = -1; } - Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); + result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf; ncopy = CURLMIN(stream->len, left); @@ -898,6 +930,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, struct Curl_easy *data_s; int32_t stream_id = frame->hd.stream_id; struct connectdata *conn = (struct connectdata *)userp; + CURLcode result; (void)flags; DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ @@ -924,6 +957,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, stream->push_headers_alloc = 10; stream->push_headers = malloc(stream->push_headers_alloc * sizeof(char *)); + if(!stream->push_headers) + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; stream->push_headers_used = 0; } else if(stream->push_headers_used == @@ -952,11 +987,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen, value)); - Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n)); - Curl_add_buffer(stream->trailer_recvbuf, name, namelen); - Curl_add_buffer(stream->trailer_recvbuf, ": ", 2); - Curl_add_buffer(stream->trailer_recvbuf, value, valuelen); - Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3); + 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); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; return 0; } @@ -969,10 +1014,16 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, stream->status_code = decode_status_code(value, valuelen); DEBUGASSERT(stream->status_code != -1); - Curl_add_buffer(stream->header_recvbuf, "HTTP/2 ", 7); - Curl_add_buffer(stream->header_recvbuf, value, valuelen); + result = Curl_add_buffer(&stream->header_recvbuf, "HTTP/2 ", 7); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; /* the space character after the status code is mandatory */ - Curl_add_buffer(stream->header_recvbuf, " \r\n", 3); + result = Curl_add_buffer(&stream->header_recvbuf, " \r\n", 3); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ if(conn->data != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); @@ -985,10 +1036,18 @@ 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 */ - Curl_add_buffer(stream->header_recvbuf, name, namelen); - Curl_add_buffer(stream->header_recvbuf, ": ", 2); - Curl_add_buffer(stream->header_recvbuf, value, valuelen); - Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); + result = Curl_add_buffer(&stream->header_recvbuf, name, namelen); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + result = Curl_add_buffer(&stream->header_recvbuf, ": ", 2); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ if(conn->data != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); @@ -1049,7 +1108,8 @@ static ssize_t data_source_read_callback(nghttp2_session *session, return nread; } -#ifdef NGHTTP2_HAS_ERROR_CALLBACK +#if defined(NGHTTP2_HAS_ERROR_CALLBACK) && \ + !defined(CURL_DISABLE_VERBOSE_STRINGS) static int error_callback(nghttp2_session *session, const char *msg, size_t len, @@ -1085,17 +1145,11 @@ void Curl_http2_done(struct connectdata *conn, bool premature) struct HTTP *http = data->req.protop; struct http_conn *httpc = &conn->proto.httpc; - if(!httpc->h2) /* not HTTP/2 ? */ - return; - - if(data->state.drain) - drained_transfer(data, 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); - http->header_recvbuf = NULL; /* clear the pointer */ - Curl_add_buffer_free(http->trailer_recvbuf); - http->trailer_recvbuf = NULL; /* clear the pointer */ + 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) { @@ -1106,6 +1160,12 @@ void Curl_http2_done(struct connectdata *conn, bool premature) } } + 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, @@ -1167,7 +1227,9 @@ CURLcode Curl_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); @@ -1204,7 +1266,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_add_buffer_free(&req); return CURLE_FAILED_INIT; } conn->proto.httpc.binlen = binlen; @@ -1212,11 +1274,11 @@ 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_add_buffer_free(&req); return result; } - result = Curl_add_bufferf(req, + result = Curl_add_bufferf(&req, "Connection: Upgrade, HTTP2-Settings\r\n" "Upgrade: %s\r\n" "HTTP2-Settings: %s\r\n", @@ -2060,8 +2122,11 @@ CURLcode Curl_http2_setup(struct connectdata *conn) stream->stream_id = -1; - if(!stream->header_recvbuf) + if(!stream->header_recvbuf) { stream->header_recvbuf = Curl_add_buffer_init(); + if(!stream->header_recvbuf) + return CURLE_OUT_OF_MEMORY; + } if((conn->handler == &Curl_handler_http2_ssl) || (conn->handler == &Curl_handler_http2)) @@ -2073,8 +2138,10 @@ CURLcode Curl_http2_setup(struct connectdata *conn) conn->handler = &Curl_handler_http2; result = Curl_http2_init(conn); - if(result) + if(result) { + Curl_add_buffer_free(&stream->header_recvbuf); return result; + } infof(conn->data, "Using HTTP2, server supports multi-use\n"); stream->upload_left = 0; diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h index 21cd9b8..4492ec2 100644 --- a/Utilities/cmcurl/lib/http2.h +++ b/Utilities/cmcurl/lib/http2.h @@ -77,4 +77,3 @@ void Curl_http2_cleanup_dependencies(struct Curl_easy *data); #endif #endif /* HEADER_CURL_HTTP2_H */ - diff --git a/Utilities/cmcurl/lib/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h index 3a8b4dd..b969c55 100644 --- a/Utilities/cmcurl/lib/http_chunks.h +++ b/Utilities/cmcurl/lib/http_chunks.h @@ -88,4 +88,3 @@ struct Curl_chunker { }; #endif /* HEADER_CURL_HTTP_CHUNKS_H */ - diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index c8c445b..2e0d92e 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -222,7 +222,7 @@ static CURLcode CONNECT(struct connectdata *conn, host_port = aprintf("%s:%d", hostname, remote_port); if(!host_port) { - Curl_add_buffer_free(req_buffer); + Curl_add_buffer_free(&req_buffer); return CURLE_OUT_OF_MEMORY; } @@ -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_add_buffer_free(&req_buffer); 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_add_buffer_free(&req_buffer); return CURLE_OUT_OF_MEMORY; } } @@ -267,7 +267,7 @@ static CURLcode CONNECT(struct connectdata *conn, useragent = conn->allocptr.uagent; result = - Curl_add_bufferf(req_buffer, + Curl_add_bufferf(&req_buffer, "CONNECT %s HTTP/%s\r\n" "%s" /* Host: */ "%s" /* Proxy-Authorization */ @@ -290,13 +290,13 @@ static CURLcode CONNECT(struct connectdata *conn, if(!result) /* CRLF terminate the request */ - result = Curl_add_bufferf(req_buffer, "\r\n"); + result = Curl_add_bufferf(&req_buffer, "\r\n"); if(!result) { /* Send the connect request to the proxy */ /* BLOCKING */ result = - Curl_add_buffer_send(req_buffer, conn, + Curl_add_buffer_send(&req_buffer, conn, &data->info.request_size, 0, sockindex); } req_buffer = NULL; @@ -304,7 +304,7 @@ static CURLcode CONNECT(struct connectdata *conn, failf(data, "Failed sending CONNECT to proxy"); } - Curl_add_buffer_free(req_buffer); + Curl_add_buffer_free(&req_buffer); if(result) return result; diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index 942fe7d..3ef8909 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c @@ -159,7 +159,8 @@ const struct Curl_handler Curl_handler_imaps = { ZERO_NULL, /* connection_check */ PORT_IMAPS, /* defport */ CURLPROTO_IMAPS, /* protocol */ - PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_URLOPTIONS }; #endif @@ -421,7 +422,6 @@ static CURLcode imap_perform_capability(struct connectdata *conn) { CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; - imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */ imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */ imapc->tls_supported = FALSE; /* Clear the TLS capability */ @@ -683,24 +683,37 @@ static CURLcode imap_perform_fetch(struct connectdata *conn) { CURLcode result = CURLE_OK; struct IMAP *imap = conn->data->req.protop; - /* Check we have a UID */ - if(!imap->uid) { - failf(conn->data, "Cannot FETCH without a UID."); - return CURLE_URL_MALFORMAT; + if(imap->uid) { + + /* Send the FETCH command */ + if(imap->partial) + result = imap_sendf(conn, "UID FETCH %s BODY[%s]<%s>", + imap->uid, + imap->section ? imap->section : "", + imap->partial); + else + result = imap_sendf(conn, "UID FETCH %s BODY[%s]", + imap->uid, + imap->section ? imap->section : ""); + } + else if(imap->mindex) { + + /* Send the FETCH command */ + if(imap->partial) + result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>", + imap->mindex, + imap->section ? imap->section : "", + imap->partial); + else + result = imap_sendf(conn, "FETCH %s BODY[%s]", + imap->mindex, + imap->section ? imap->section : ""); + } + else { + failf(conn->data, "Cannot FETCH without a UID."); + return CURLE_URL_MALFORMAT; } - - /* Send the FETCH command */ - if(imap->partial) - result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>", - imap->uid, - imap->section ? imap->section : "", - imap->partial); - else - result = imap_sendf(conn, "FETCH %s BODY[%s]", - imap->uid, - imap->section ? imap->section : ""); - if(!result) state(conn, IMAP_FETCH); @@ -1464,9 +1477,10 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status, result = status; /* use the already set error code */ } else if(!data->set.connect_only && !imap->custom && - (imap->uid || data->set.upload || + (imap->uid || imap->mindex || data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)) { /* Handle responses after FETCH or APPEND transfer has finished */ + if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE) state(conn, IMAP_FETCH_FINAL); else { @@ -1490,6 +1504,7 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status, Curl_safefree(imap->mailbox); Curl_safefree(imap->uidvalidity); Curl_safefree(imap->uid); + Curl_safefree(imap->mindex); Curl_safefree(imap->section); Curl_safefree(imap->partial); Curl_safefree(imap->query); @@ -1543,14 +1558,14 @@ static CURLcode imap_perform(struct connectdata *conn, bool *connected, else if(imap->custom && (selected || !imap->mailbox)) /* Custom command using the same mailbox or no mailbox */ result = imap_perform_list(conn); - else if(!imap->custom && selected && imap->uid) + else if(!imap->custom && selected && (imap->uid || imap->mindex)) /* FETCH from the same mailbox */ result = imap_perform_fetch(conn); else if(!imap->custom && selected && imap->query) /* SEARCH the current mailbox */ result = imap_perform_search(conn); else if(imap->mailbox && !selected && - (imap->custom || imap->uid || imap->query)) + (imap->custom || imap->uid || imap->mindex || imap->query)) /* SELECT the mailbox */ result = imap_perform_select(conn); else @@ -1702,8 +1717,6 @@ static CURLcode imap_regular_transfer(struct connectdata *conn, static CURLcode imap_setup_connection(struct connectdata *conn) { - struct Curl_easy *data = conn->data; - /* Initialise the IMAP layer */ CURLcode result = imap_init(conn); if(result) @@ -1711,7 +1724,6 @@ static CURLcode imap_setup_connection(struct connectdata *conn) /* Clear the TLS upgraded flag */ conn->tls_upgraded = FALSE; - data->state.path++; /* don't include the initial slash */ return CURLE_OK; } @@ -1944,7 +1956,7 @@ static CURLcode imap_parse_url_path(struct connectdata *conn) CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; struct IMAP *imap = data->req.protop; - const char *begin = data->state.path; + const char *begin = &data->state.up.path[1]; /* skip leading slash */ const char *ptr = begin; /* See how much of the URL is a valid path and decode it */ @@ -2016,6 +2028,13 @@ static CURLcode imap_parse_url_path(struct connectdata *conn) imap->uid = value; value = NULL; } + else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) { + if(valuelen > 0 && value[valuelen - 1] == '/') + value[valuelen - 1] = '\0'; + + imap->mindex = value; + value = NULL; + } else if(strcasecompare(name, "SECTION") && !imap->section) { if(valuelen > 0 && value[valuelen - 1] == '/') value[valuelen - 1] = '\0'; @@ -2043,17 +2062,10 @@ static CURLcode imap_parse_url_path(struct connectdata *conn) /* Does the URL contain a query parameter? Only valid when we have a mailbox and no UID as per RFC-5092 */ - if(imap->mailbox && !imap->uid && *ptr == '?') { - /* Find the length of the query parameter */ - begin = ++ptr; - while(imap_is_bchar(*ptr)) - ptr++; - - /* Decode the query parameter */ - result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL, - TRUE); - if(result) - return result; + if(imap->mailbox && !imap->uid && !imap->mindex) { + /* Get the query parameter, URL decoded */ + (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query, + CURLU_URLDECODE); } /* Any extra stuff at the end of the URL is an error */ diff --git a/Utilities/cmcurl/lib/imap.h b/Utilities/cmcurl/lib/imap.h index 9fc4ff5..0efcfd2 100644 --- a/Utilities/cmcurl/lib/imap.h +++ b/Utilities/cmcurl/lib/imap.h @@ -58,6 +58,7 @@ struct IMAP { char *mailbox; /* Mailbox to select */ char *uidvalidity; /* UIDVALIDITY to check in select */ char *uid; /* Message UID to fetch */ + char *mindex; /* Index in mail box of mail to fetch */ char *section; /* Message SECTION to fetch */ char *partial; /* Message PARTIAL to fetch */ char *query; /* Query to search for */ diff --git a/Utilities/cmcurl/lib/inet_ntop.h b/Utilities/cmcurl/lib/inet_ntop.h index 9f44612..d150bb6 100644 --- a/Utilities/cmcurl/lib/inet_ntop.h +++ b/Utilities/cmcurl/lib/inet_ntop.h @@ -35,4 +35,3 @@ char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size); #endif #endif /* HEADER_CURL_INET_NTOP_H */ - diff --git a/Utilities/cmcurl/lib/inet_pton.h b/Utilities/cmcurl/lib/inet_pton.h index e216f4e..0209b9b 100644 --- a/Utilities/cmcurl/lib/inet_pton.h +++ b/Utilities/cmcurl/lib/inet_pton.h @@ -37,4 +37,3 @@ int Curl_inet_pton(int, const char *, void *); #endif #endif /* HEADER_CURL_INET_PTON_H */ - diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c index 59c0d71..147ab02 100644 --- a/Utilities/cmcurl/lib/krb5.c +++ b/Utilities/cmcurl/lib/krb5.c @@ -206,7 +206,7 @@ krb5_auth(void *app_data, struct connectdata *conn) if(maj != GSS_S_COMPLETE) { gss_release_name(&min, &gssname); if(service == srv_host) { - Curl_failf(data, "Error importing service name %s@%s", service, host); + failf(data, "Error importing service name %s@%s", service, host); return AUTH_ERROR; } service = srv_host; @@ -265,6 +265,7 @@ krb5_auth(void *app_data, struct connectdata *conn) result = CURLE_OUT_OF_MEMORY; free(p); + free(cmd); if(result) { ret = -2; @@ -290,8 +291,7 @@ krb5_auth(void *app_data, struct connectdata *conn) (unsigned char **)&_gssresp.value, &_gssresp.length); if(result) { - Curl_failf(data, "base64-decoding: %s", - curl_easy_strerror(result)); + failf(data, "base64-decoding: %s", curl_easy_strerror(result)); ret = AUTH_CONTINUE; break; } diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 4d8f4fa..ceaa71d 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -474,7 +474,13 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) #endif } if(rc != 0) { - failf(data, "LDAP local: ldap_simple_bind_s %s", ldap_err2string(rc)); +#ifdef USE_WIN32_LDAP + failf(data, "LDAP local: bind via ldap_win_bind %s", + ldap_err2string(rc)); +#else + failf(data, "LDAP local: bind via ldap_simple_bind_s %s", + ldap_err2string(rc)); +#endif result = CURLE_LDAP_CANNOT_BIND; goto quit; } @@ -838,9 +844,9 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) size_t i; if(!conn->data || - !conn->data->state.path || - conn->data->state.path[0] != '/' || - !checkprefix("LDAP", conn->data->change.url)) + !conn->data->state.up.path || + conn->data->state.up.path[0] != '/' || + !strcasecompare("LDAP", conn->data->state.up.scheme)) return LDAP_INVALID_SYNTAX; ludp->lud_scope = LDAP_SCOPE_BASE; @@ -848,7 +854,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp) ludp->lud_host = conn->host.name; /* Duplicate the path */ - p = path = strdup(conn->data->state.path + 1); + p = path = strdup(conn->data->state.up.path + 1); if(!path) return LDAP_NO_MEMORY; diff --git a/Utilities/cmcurl/lib/llist.h b/Utilities/cmcurl/lib/llist.h index 6b644b9..b9d4c89 100644 --- a/Utilities/cmcurl/lib/llist.h +++ b/Utilities/cmcurl/lib/llist.h @@ -51,4 +51,3 @@ 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 2bb7dcc..d350602 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c @@ -3,7 +3,7 @@ * MD4 Message-Digest Algorithm (RFC 1320). * * Homepage: - http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 + https://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 * * Author: * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c index b819d39..45f45bb 100644 --- a/Utilities/cmcurl/lib/md5.c +++ b/Utilities/cmcurl/lib/md5.c @@ -177,7 +177,7 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) * MD5 Message-Digest Algorithm (RFC 1321). * * Homepage: - http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 * * Author: * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index 0caf943..0db2a97 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -347,6 +347,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ Curl_llist_init(&multi->pending, multi_freeamsg); multi->max_pipeline_length = 5; + multi->pipelining = CURLPIPE_MULTIPLEX; /* -1 means it not set by user, use the default value */ multi->maxconnects = -1; @@ -491,6 +492,8 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->state.conn_cache->closure_handle->set.timeout = data->set.timeout; data->state.conn_cache->closure_handle->set.server_response_timeout = data->set.server_response_timeout; + data->state.conn_cache->closure_handle->set.no_signal = + data->set.no_signal; update_timer(multi); return CURLM_OK; @@ -541,10 +544,8 @@ static CURLcode multi_done(struct connectdata **connp, Curl_getoff_all_pipelines(data, conn); /* Cleanup possible redirect junk */ - free(data->req.newurl); - data->req.newurl = NULL; - free(data->req.location); - data->req.location = NULL; + Curl_safefree(data->req.newurl); + Curl_safefree(data->req.location); switch(status) { case CURLE_ABORTED_BY_CALLBACK: @@ -656,7 +657,6 @@ static CURLcode multi_done(struct connectdata **connp, cache here, and therefore cannot be used from this point on */ Curl_free_request_state(data); - return result; } @@ -905,7 +905,7 @@ static int multi_getsock(struct Curl_easy *data, return 0; case CURLM_STATE_WAITRESOLVE: - return Curl_resolver_getsock(data->easy_conn, socks, numsocks); + return Curl_resolv_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_PROTOCONNECT: case CURLM_STATE_SENDPROTOCONNECT: @@ -1009,13 +1009,6 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - /* If the internally desired timeout is actually shorter than requested from - the outside, then use the shorter time! But only if the internal timer - is actually larger than -1! */ - (void)multi_timeout(multi, &timeout_internal); - if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) - timeout_ms = (int)timeout_internal; - /* Count up how many fds we have from the multi handle */ data = multi->easyp; while(data) { @@ -1040,6 +1033,13 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi, data = data->next; /* check next handle */ } + /* If the internally desired timeout is actually shorter than requested from + the outside, then use the shorter time! But only if the internal timer + is actually larger than -1! */ + (void)multi_timeout(multi, &timeout_internal); + if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) + timeout_ms = (int)timeout_internal; + curlfds = nfds; /* number of internal file descriptors */ nfds += extra_nfds; /* add the externally provided ones */ @@ -1235,7 +1235,7 @@ static CURLcode multi_reconnect_request(struct connectdata **connp) return result; /* Resolved, continue with the connection */ - result = Curl_async_resolved(conn, &protocol_done); + result = Curl_once_resolved(conn, &protocol_done); if(result) return result; } @@ -1511,7 +1511,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } if(!dns) - result = Curl_resolver_is_resolved(data->easy_conn, &dns); + result = Curl_resolv_check(data->easy_conn, &dns); /* Update sockets here, because the socket(s) may have been closed and the application thus needs to be told, even if it @@ -1524,10 +1524,10 @@ 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_async_resolved(data->easy_conn, &protocol_connect); + result = Curl_once_resolved(data->easy_conn, &protocol_connect); if(result) - /* if Curl_async_resolved() returns failure, the connection struct + /* if Curl_once_resolved() returns failure, the connection struct is already freed and gone */ data->easy_conn = NULL; /* no more connection */ else { @@ -1608,7 +1608,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_SENDPROTOCONNECT: result = Curl_protocol_connect(data->easy_conn, &protocol_connect); - if(!protocol_connect) + if(!result && !protocol_connect) /* switch to waiting state */ multistate(data, CURLM_STATE_PROTOCONNECT); else if(!result) { @@ -1707,7 +1707,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, char *newurl = NULL; followtype follow = FOLLOW_NONE; CURLcode drc; - bool retry = FALSE; drc = Curl_retry_request(data->easy_conn, &newurl); if(drc) { @@ -1715,15 +1714,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result = drc; stream_error = TRUE; } - else - retry = (newurl)?TRUE:FALSE; Curl_posttransfer(data); drc = multi_done(&data->easy_conn, result, FALSE); /* When set to retry the connection, we must to go back to * the CONNECT state */ - if(retry) { + if(newurl) { if(!drc || (drc == CURLE_SEND_ERROR)) { follow = FOLLOW_RETRY; drc = Curl_follow(data, newurl, follow); @@ -1993,6 +1990,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, rc = CURLM_CALL_MULTI_PERFORM; } } + free(newurl); } else { /* after the transfer is done, go DONE */ @@ -2004,18 +2002,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, newurl = data->req.location; data->req.location = NULL; result = Curl_follow(data, newurl, FOLLOW_FAKE); - if(result) + free(newurl); + if(result) { stream_error = TRUE; + result = multi_done(&data->easy_conn, result, TRUE); + } } - multistate(data, CURLM_STATE_DONE); - rc = CURLM_CALL_MULTI_PERFORM; + if(!result) { + multistate(data, CURLM_STATE_DONE); + rc = CURLM_CALL_MULTI_PERFORM; + } } } else if(comeback) rc = CURLM_CALL_MULTI_PERFORM; - - free(newurl); break; } @@ -2131,15 +2132,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } if(CURLM_STATE_COMPLETED == data->mstate) { - /* now fill in the Curl_message with this info */ - msg = &data->msg; + if(data->set.fmultidone) { + /* signal via callback instead */ + data->set.fmultidone(data, result); + } + else { + /* now fill in the Curl_message with this info */ + msg = &data->msg; - msg->extmsg.msg = CURLMSG_DONE; - msg->extmsg.easy_handle = data; - msg->extmsg.data.result = result; + msg->extmsg.msg = CURLMSG_DONE; + msg->extmsg.easy_handle = data; + msg->extmsg.data.result = result; - rc = multi_addmsg(multi, msg); - DEBUGASSERT(!data->easy_conn); + rc = multi_addmsg(multi, msg); + DEBUGASSERT(!data->easy_conn); + } multistate(data, CURLM_STATE_MSGSENT); } } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); @@ -2705,7 +2712,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, multi->push_userp = va_arg(param, void *); break; case CURLMOPT_PIPELINING: - multi->pipelining = va_arg(param, long); + multi->pipelining = va_arg(param, long) & CURLPIPE_MULTIPLEX; break; case CURLMOPT_TIMERFUNCTION: multi->timer_cb = va_arg(param, curl_multi_timer_callback); diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c index a407bda..1724b35 100644 --- a/Utilities/cmcurl/lib/netrc.c +++ b/Utilities/cmcurl/lib/netrc.c @@ -57,7 +57,11 @@ int Curl_parsenetrc(const char *host, { FILE *file; int retcode = 1; - int specific_login = (*loginp && **loginp != 0); + 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; @@ -125,7 +129,7 @@ int Curl_parsenetrc(const char *host, continue; while(!done && tok) { - if((*loginp && **loginp) && (*passwordp && **passwordp)) { + if((login && *login) && (password && *password)) { done = TRUE; break; } @@ -158,26 +162,34 @@ int Curl_parsenetrc(const char *host, /* we are now parsing sub-keywords concerning "our" host */ if(state_login) { if(specific_login) { - state_our_login = strcasecompare(*loginp, tok); + state_our_login = strcasecompare(login, tok); } else { - free(*loginp); - *loginp = strdup(tok); - if(!*loginp) { + if(login_alloc) { + free(login); + login_alloc = FALSE; + } + login = strdup(tok); + if(!login) { retcode = -1; /* allocation failed */ goto out; } + login_alloc = TRUE; } state_login = 0; } else if(state_password) { if(state_our_login || !specific_login) { - free(*passwordp); - *passwordp = strdup(tok); - if(!*passwordp) { + if(password_alloc) { + free(password); + password_alloc = FALSE; + } + password = strdup(tok); + if(!password) { retcode = -1; /* allocation failed */ goto out; } + password_alloc = TRUE; } state_password = 0; } @@ -198,6 +210,24 @@ int Curl_parsenetrc(const char *host, } /* while fgets() */ out: + if(!retcode) { + if(login_alloc) { + if(*loginp) + free(*loginp); + *loginp = login; + } + if(password_alloc) { + if(*passwordp) + free(*passwordp); + *passwordp = password; + } + } + else { + if(login_alloc) + free(login); + if(password_alloc) + free(password); + } fclose(file); } diff --git a/Utilities/cmcurl/lib/nonblock.c b/Utilities/cmcurl/lib/nonblock.c index 5959281..4d105c1 100644 --- a/Utilities/cmcurl/lib/nonblock.c +++ b/Utilities/cmcurl/lib/nonblock.c @@ -48,7 +48,8 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ int nonblock /* TRUE or FALSE */) { #if defined(USE_BLOCKING_SOCKETS) - + (void)sockfd; + (void)nonblock; return 0; /* returns success */ #elif defined(HAVE_FCNTL_O_NONBLOCK) diff --git a/Utilities/cmcurl/lib/nonblock.h b/Utilities/cmcurl/lib/nonblock.h index 98cdc25..eb18ea1 100644 --- a/Utilities/cmcurl/lib/nonblock.h +++ b/Utilities/cmcurl/lib/nonblock.h @@ -28,4 +28,3 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ int nonblock /* TRUE or FALSE */); #endif /* HEADER_CURL_NONBLOCK_H */ - diff --git a/Utilities/cmcurl/lib/nwlib.c b/Utilities/cmcurl/lib/nwlib.c index 215d933..7bf5f51 100644 --- a/Utilities/cmcurl/lib/nwlib.c +++ b/Utilities/cmcurl/lib/nwlib.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -195,7 +195,7 @@ int GetOrSetUpData(int id, libdata_t **appData, if(!app_data->tenbytes || !app_data->lock) { if(app_data->lock) NXMutexFree(app_data->lock); - + free(app_data->tenbytes); free(app_data); app_data = (libdata_t *) NULL; err = ENOMEM; @@ -213,6 +213,9 @@ int GetOrSetUpData(int id, libdata_t **appData, err = set_app_data(gLibId, app_data); if(err) { + if(app_data->lock) + NXMutexFree(app_data->lock); + free(app_data->tenbytes); free(app_data); app_data = (libdata_t *) NULL; err = ENOMEM; diff --git a/Utilities/cmcurl/lib/parsedate.h b/Utilities/cmcurl/lib/parsedate.h index 2e59eb1..8dc3b90 100644 --- a/Utilities/cmcurl/lib/parsedate.h +++ b/Utilities/cmcurl/lib/parsedate.h @@ -28,4 +28,3 @@ extern const char * const Curl_month[12]; CURLcode Curl_gmtime(time_t intime, struct tm *store); #endif /* HEADER_CURL_PARSEDATE_H */ - diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index cd994f6..5e0fd22 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c @@ -1303,8 +1303,6 @@ static CURLcode pop3_regular_transfer(struct connectdata *conn, static CURLcode pop3_setup_connection(struct connectdata *conn) { - struct Curl_easy *data = conn->data; - /* Initialise the POP3 layer */ CURLcode result = pop3_init(conn); if(result) @@ -1312,7 +1310,6 @@ static CURLcode pop3_setup_connection(struct connectdata *conn) /* Clear the TLS upgraded flag */ conn->tls_upgraded = FALSE; - data->state.path++; /* don't include the initial slash */ return CURLE_OK; } @@ -1387,7 +1384,7 @@ static CURLcode pop3_parse_url_path(struct connectdata *conn) /* The POP3 struct is already initialised in pop3_connect() */ struct Curl_easy *data = conn->data; struct POP3 *pop3 = data->req.protop; - const char *path = data->state.path; + 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); diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h index 92dbcbd..3515ac6 100644 --- a/Utilities/cmcurl/lib/progress.h +++ b/Utilities/cmcurl/lib/progress.h @@ -62,4 +62,3 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, #define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */ #endif /* HEADER_CURL_PROGRESS_H */ - diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index 1dc2504..6ee45fe 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -174,6 +174,8 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, return result; while(num) { + /* clang-tidy warns on this line without this comment: */ + /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */ *rnd++ = hex[(*bufp & 0xF0)>>4]; *rnd++ = hex[*bufp & 0x0F]; bufp++; diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index 182ee29..01dfce6 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c @@ -462,7 +462,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) return CURLE_OUT_OF_MEMORY; result = - Curl_add_bufferf(req_buffer, + 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); @@ -474,7 +474,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_add_bufferf(&req_buffer, "Session: %s\r\n", p_session_id); if(result) return result; } @@ -482,7 +482,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* * Shared HTTP-like options */ - result = Curl_add_bufferf(req_buffer, + result = Curl_add_bufferf(&req_buffer, "%s" /* transport */ "%s" /* accept */ "%s" /* accept-encoding */ @@ -541,9 +541,10 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* As stated in the http comments, it is probably not wise to * 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)); + result = + Curl_add_bufferf(&req_buffer, + "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", + (data->set.upload ? putsize : postsize)); if(result) return result; } @@ -551,8 +552,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_add_bufferf(&req_buffer, + "Content-Type: text/parameters\r\n"); if(result) return result; } @@ -560,8 +561,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_add_bufferf(&req_buffer, + "Content-Type: application/sdp\r\n"); if(result) return result; } @@ -579,19 +580,19 @@ 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_add_buffer(&req_buffer, "\r\n", 2); if(result) return result; if(postsize > 0) { - result = Curl_add_buffer(req_buffer, data->set.postfields, + result = Curl_add_buffer(&req_buffer, data->set.postfields, (size_t)postsize); if(result) return result; } /* issue the request */ - result = Curl_add_buffer_send(req_buffer, conn, + result = Curl_add_buffer_send(&req_buffer, conn, &data->info.request_size, 0, FIRSTSOCKET); if(result) { failf(data, "Failed sending RTSP request"); diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h index 8375a53..2f9cc32 100644 --- a/Utilities/cmcurl/lib/rtsp.h +++ b/Utilities/cmcurl/lib/rtsp.h @@ -64,4 +64,3 @@ struct RTSP { #endif /* HEADER_CURL_RTSP_H */ - diff --git a/Utilities/cmcurl/lib/security.c b/Utilities/cmcurl/lib/security.c index 4034115..c278406 100644 --- a/Utilities/cmcurl/lib/security.c +++ b/Utilities/cmcurl/lib/security.c @@ -61,7 +61,9 @@ #include "strcase.h" #include "warnless.h" #include "strdup.h" -/* 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" static const struct { @@ -422,7 +424,7 @@ static int sec_set_protection_level(struct connectdata *conn) if(!conn->sec_complete) { infof(conn->data, "Trying to change the protection level after the" - "completion of the data exchange.\n"); + " completion of the data exchange.\n"); return -1; } diff --git a/Utilities/cmcurl/lib/select.h b/Utilities/cmcurl/lib/select.h index 4351786..9a1ba45 100644 --- a/Utilities/cmcurl/lib/select.h +++ b/Utilities/cmcurl/lib/select.h @@ -113,4 +113,3 @@ int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes, #endif #endif /* HEADER_CURL_SELECT_H */ - diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h index 7627fe6..c68b017 100644 --- a/Utilities/cmcurl/lib/sendf.h +++ b/Utilities/cmcurl/lib/sendf.h @@ -36,7 +36,7 @@ void Curl_failf(struct Curl_easy *, const char *fmt, ...); #elif defined(HAVE_VARIADIC_MACROS_GCC) #define infof(x...) Curl_nop_stmt #else -#define infof (void) +#error "missing VARIADIC macro define, fix and rebuild!" #endif #else /* CURL_DISABLE_VERBOSE_STRINGS */ diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index 5c5f4b3..22956a2 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c @@ -127,9 +127,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, data->set.dns_cache_timeout = arg; break; case CURLOPT_DNS_USE_GLOBAL_CACHE: +#if 0 /* deprecated */ /* remember we want this enabled */ arg = va_arg(param, long); data->set.global_dns_cache = (0 != arg) ? TRUE : FALSE; +#endif break; case CURLOPT_SSL_CIPHER_LIST: /* set a list of cipher we want to use in the SSL connection */ @@ -841,6 +843,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, #else if(arg > CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) return CURLE_UNSUPPORTED_PROTOCOL; + if(arg == CURL_HTTP_VERSION_NONE) + arg = CURL_HTTP_VERSION_2TLS; #endif data->set.httpversion = arg; break; @@ -1936,6 +1940,22 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, break; + case CURLOPT_UPLOAD_BUFFERSIZE: + /* + * The application kindly asks for a differently sized upload buffer. + * Cap it to sensible. + */ + arg = va_arg(param, long); + + if(arg > UPLOADBUFFER_MAX) + arg = UPLOADBUFFER_MAX; + else if(arg < UPLOADBUFFER_MIN) + arg = UPLOADBUFFER_MIN; + + data->set.upload_buffer_size = arg; + Curl_safefree(data->state.ulbuf); /* force a realloc next opportunity */ + break; + case CURLOPT_NOSIGNAL: /* * The application asks not to set any signal() or alarm() handlers, @@ -2599,6 +2619,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, data->set.disallow_username_in_url = (0 != va_arg(param, long)) ? TRUE : FALSE; break; + case CURLOPT_DOH_URL: + result = Curl_setstropt(&data->set.str[STRING_DOH], + va_arg(param, char *)); + data->set.doh = data->set.str[STRING_DOH]?TRUE:FALSE; + break; + case CURLOPT_UPKEEP_INTERVAL_MS: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.upkeep_interval_ms = arg; + break; default: /* unknown tag and its companion, just ignore: */ result = CURLE_UNKNOWN_OPTION; diff --git a/Utilities/cmcurl/lib/slist.c b/Utilities/cmcurl/lib/slist.c index e5adc0e..392b84d 100644 --- a/Utilities/cmcurl/lib/slist.c +++ b/Utilities/cmcurl/lib/slist.c @@ -142,4 +142,3 @@ void curl_slist_free_all(struct curl_slist *list) item = next; } while(next); } - diff --git a/Utilities/cmcurl/lib/slist.h b/Utilities/cmcurl/lib/slist.h index b3f498c..d73dbf6 100644 --- a/Utilities/cmcurl/lib/slist.h +++ b/Utilities/cmcurl/lib/slist.h @@ -37,4 +37,3 @@ struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, char *data); #endif /* HEADER_CURL_SLIST_H */ - diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index e4b18fc..e4f266e 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c @@ -610,7 +610,8 @@ static CURLcode smb_send_and_recv(struct connectdata *conn, void **msg) /* Check if there is data in the transfer buffer */ if(!smbc->send_size && smbc->upload_size) { - size_t nread = smbc->upload_size > UPLOAD_BUFSIZE ? UPLOAD_BUFSIZE : + size_t nread = smbc->upload_size > conn->data->set.upload_buffer_size ? + conn->data->set.upload_buffer_size : smbc->upload_size; conn->data->req.upload_fromhere = conn->data->state.ulbuf; result = Curl_fillreadbuffer(conn, nread, &nread); @@ -968,7 +969,7 @@ static CURLcode smb_parse_url_path(struct connectdata *conn) char *slash; /* URL decode the path */ - result = Curl_urldecode(data, data->state.path, 0, &path, NULL, TRUE); + result = Curl_urldecode(data, data->state.up.path, 0, &path, NULL, TRUE); if(result) return result; diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index ecf10a4..5875623 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c @@ -1441,7 +1441,6 @@ static CURLcode smtp_regular_transfer(struct connectdata *conn, static CURLcode smtp_setup_connection(struct connectdata *conn) { - struct Curl_easy *data = conn->data; CURLcode result; /* Clear the TLS upgraded flag */ @@ -1452,8 +1451,6 @@ static CURLcode smtp_setup_connection(struct connectdata *conn) if(result) return result; - data->state.path++; /* don't include the initial slash */ - return CURLE_OK; } @@ -1507,7 +1504,7 @@ static CURLcode smtp_parse_url_path(struct connectdata *conn) /* The SMTP struct is already initialised in smtp_connect() */ struct Curl_easy *data = conn->data; struct smtp_conn *smtpc = &conn->proto.smtpc; - const char *path = data->state.path; + const char *path = &data->state.up.path[1]; /* skip leading path */ char localhost[HOSTNAME_MAX + 1]; /* Calculate the path if necessary */ @@ -1563,14 +1560,14 @@ CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread) if(!scratch || data->set.crlf) { oldscratch = scratch; - scratch = newscratch = malloc(2 * UPLOAD_BUFSIZE); + scratch = newscratch = malloc(2 * data->set.upload_buffer_size); if(!newscratch) { failf(data, "Failed to alloc scratch buffer!"); return CURLE_OUT_OF_MEMORY; } } - DEBUGASSERT(UPLOAD_BUFSIZE >= nread); + DEBUGASSERT(data->set.upload_buffer_size >= (size_t)nread); /* Have we already sent part of the EOB? */ eob_sent = smtp->eob; diff --git a/Utilities/cmcurl/lib/sockaddr.h b/Utilities/cmcurl/lib/sockaddr.h index 95ba4c3..db14680 100644 --- a/Utilities/cmcurl/lib/sockaddr.h +++ b/Utilities/cmcurl/lib/sockaddr.h @@ -40,4 +40,3 @@ struct Curl_sockaddr_storage { }; #endif /* HEADER_CURL_SOCKADDR_H */ - diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index 81f3eda..d2209ad 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c @@ -98,7 +98,7 @@ int Curl_blockread_all(struct connectdata *conn, /* connection data */ * destination server. * * Reference : -* http://socks.permeo.com/protocol/socks4.protocol +* https://www.openssh.com/txt/socks4.protocol * * Note : * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" @@ -789,4 +789,3 @@ CURLcode Curl_SOCKS5(const char *proxy_user, } #endif /* CURL_DISABLE_PROXY */ - diff --git a/Utilities/cmcurl/lib/socks.h b/Utilities/cmcurl/lib/socks.h index 348707e..daa07c1 100644 --- a/Utilities/cmcurl/lib/socks.h +++ b/Utilities/cmcurl/lib/socks.h @@ -73,4 +73,3 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, #endif /* CURL_DISABLE_PROXY */ #endif /* HEADER_CURL_SOCKS_H */ - diff --git a/Utilities/cmcurl/lib/splay.c b/Utilities/cmcurl/lib/splay.c index c54a63b..baf07e0 100644 --- a/Utilities/cmcurl/lib/splay.c +++ b/Utilities/cmcurl/lib/splay.c @@ -274,4 +274,3 @@ int Curl_splayremovebyaddr(struct Curl_tree *t, return 0; } - diff --git a/Utilities/cmcurl/lib/ssh.c b/Utilities/cmcurl/lib/ssh.c index a4b2ca4..da89619 100644 --- a/Utilities/cmcurl/lib/ssh.c +++ b/Utilities/cmcurl/lib/ssh.c @@ -2926,7 +2926,7 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done) int rc; ssh->kh = libssh2_knownhost_init(ssh->ssh_session); if(!ssh->kh) { - /* eeek. TODO: free the ssh_session! */ + libssh2_session_free(ssh->ssh_session); return CURLE_FAILED_INIT; } diff --git a/Utilities/cmcurl/lib/strdup.c b/Utilities/cmcurl/lib/strdup.c index 19cb044..51e7978 100644 --- a/Utilities/cmcurl/lib/strdup.c +++ b/Utilities/cmcurl/lib/strdup.c @@ -81,7 +81,7 @@ void *Curl_memdup(const void *src, size_t length) * Curl_saferealloc(ptr, size) * * Does a normal realloc(), but will free the data pointer if the realloc - * fails. If 'size' is zero, it will free the data and return a failure. + * fails. If 'size' is non-zero, it will free the data and return a failure. * * This convenience function is provided and used to help us avoid a common * mistake pattern when we could pass in a zero, catch the NULL return and end diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index 0295d6c..47ef44a 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c @@ -191,9 +191,6 @@ curl_easy_strerror(CURLcode error) case CURLE_TELNET_OPTION_SYNTAX : return "Malformed telnet option"; - case CURLE_PEER_FAILED_VERIFICATION: - return "SSL peer certificate or SSH remote key was not OK"; - case CURLE_GOT_NOTHING: return "Server returned nothing (no headers, no data)"; @@ -218,9 +215,8 @@ curl_easy_strerror(CURLcode error) case CURLE_SSL_CIPHER: return "Couldn't use specified SSL cipher"; - case CURLE_SSL_CACERT: - return "Peer certificate cannot be authenticated with given CA " - "certificates"; + case CURLE_PEER_FAILED_VERIFICATION: + return "SSL peer certificate or SSH remote key was not OK"; case CURLE_SSL_CACERT_BADFILE: return "Problem with the SSL CA cert (path? access rights?)"; @@ -324,6 +320,7 @@ curl_easy_strerror(CURLcode error) case CURLE_OBSOLETE44: case CURLE_OBSOLETE46: case CURLE_OBSOLETE50: + case CURLE_OBSOLETE51: case CURLE_OBSOLETE57: case CURL_LAST: break; diff --git a/Utilities/cmcurl/lib/telnet.h b/Utilities/cmcurl/lib/telnet.h index 419a399..668a78a 100644 --- a/Utilities/cmcurl/lib/telnet.h +++ b/Utilities/cmcurl/lib/telnet.h @@ -26,4 +26,3 @@ extern const struct Curl_handler Curl_handler_telnet; #endif #endif /* HEADER_CURL_TELNET_H */ - diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index e5bc80b..5b74e8e 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c @@ -485,7 +485,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) /* As RFC3617 describes the separator slash is not actually part of the file name so we skip the always-present first letter of the path string. */ - result = Curl_urldecode(data, &state->conn->data->state.path[1], 0, + result = Curl_urldecode(data, &state->conn->data->state.up.path[1], 0, &filename, NULL, FALSE); if(result) return result; @@ -1374,7 +1374,7 @@ static CURLcode tftp_setup_connection(struct connectdata * conn) /* TFTP URLs support an extension like ";mode=<typecode>" that * we'll try to get now! */ - type = strstr(data->state.path, ";mode="); + type = strstr(data->state.up.path, ";mode="); if(!type) type = strstr(conn->host.rawalloc, ";mode="); diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h index c2325b2..1335f64 100644 --- a/Utilities/cmcurl/lib/tftp.h +++ b/Utilities/cmcurl/lib/tftp.h @@ -26,4 +26,3 @@ extern const struct Curl_handler Curl_handler_tftp; #endif #endif /* HEADER_CURL_TFTP_H */ - diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c index f4bf835..dce1a76 100644 --- a/Utilities/cmcurl/lib/timeval.c +++ b/Utilities/cmcurl/lib/timeval.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -33,7 +33,8 @@ struct curltime Curl_now(void) */ struct curltime now; #if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ - (_WIN32_WINNT < _WIN32_WINNT_VISTA) + (_WIN32_WINNT < _WIN32_WINNT_VISTA) || \ + (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) DWORD milliseconds = GetTickCount(); now.tv_sec = milliseconds / 1000; now.tv_usec = (milliseconds % 1000) * 1000; @@ -60,7 +61,23 @@ struct curltime Curl_now(void) struct timeval now; struct curltime cnow; struct timespec tsnow; - if(0 == clock_gettime(CLOCK_MONOTONIC, &tsnow)) { + + /* + ** clock_gettime() may be defined by Apple's SDK as weak symbol thus + ** code compiles but fails during run-time if clock_gettime() is + ** called on unsupported OS version. + */ +#if defined(__APPLE__) && (HAVE_BUILTIN_AVAILABLE == 1) + bool have_clock_gettime = FALSE; + if(__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) + have_clock_gettime = TRUE; +#endif + + if( +#if defined(__APPLE__) && (HAVE_BUILTIN_AVAILABLE == 1) + have_clock_gettime && +#endif + (0 == clock_gettime(CLOCK_MONOTONIC, &tsnow))) { cnow.tv_sec = tsnow.tv_sec; cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000); } diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 7159d5c..05ba862 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -75,6 +75,7 @@ #include "http2.h" #include "mime.h" #include "strcase.h" +#include "urlapi-int.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -362,7 +363,7 @@ static int data_pending(const struct connectdata *conn) return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) || #if defined(USE_NGHTTP2) Curl_ssl_data_pending(conn, FIRSTSOCKET) || - /* For HTTP/2, we may read up everything including responde body + /* For HTTP/2, we may read up everything including response body with header fields in Curl_http_readwrite_headers. If no content-length is provided, curl waits for the connection close, which we emulate it using conn->proto.httpc.closed = @@ -566,7 +567,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, infof(data, "Rewinding stream by : %zd" " bytes on url %s (zero-length body)\n", - nread, data->state.path); + nread, data->state.up.path); read_rewind(conn, (size_t)nread); } else { @@ -574,7 +575,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, "Excess found in a non pipelined read:" " excess = %zd" " url = %s (zero-length body)\n", - nread, data->state.path); + nread, data->state.up.path); } } @@ -743,7 +744,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, " bytes on url %s (size = %" CURL_FORMAT_CURL_OFF_T ", maxdownload = %" CURL_FORMAT_CURL_OFF_T ", bytecount = %" CURL_FORMAT_CURL_OFF_T ", nread = %zd)\n", - excess, data->state.path, + excess, data->state.up.path, k->size, k->maxdownload, k->bytecount, nread); read_rewind(conn, excess); } @@ -878,7 +879,7 @@ static CURLcode done_sending(struct connectdata *conn, return CURLE_OK; } -#ifdef WIN32 +#if defined(WIN32) && !defined(USE_LWIPSOCK) #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B #endif @@ -959,7 +960,8 @@ static CURLcode readwrite_upload(struct Curl_easy *data, sending_http_headers = FALSE; } - result = Curl_fillreadbuffer(conn, UPLOAD_BUFSIZE, &fillcount); + result = Curl_fillreadbuffer(conn, data->set.upload_buffer_size, + &fillcount); if(result) return result; @@ -991,7 +993,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data, (data->set.crlf))) { /* Do we need to allocate a scratch buffer? */ if(!data->state.scratch) { - data->state.scratch = malloc(2 * UPLOAD_BUFSIZE); + data->state.scratch = malloc(2 * data->set.upload_buffer_size); if(!data->state.scratch) { failf(data, "Failed to alloc scratch buffer!"); @@ -1453,314 +1455,11 @@ CURLcode Curl_posttransfer(struct Curl_easy *data) return CURLE_OK; } -#ifndef CURL_DISABLE_HTTP -/* - * Find the separator at the end of the host name, or the '?' in cases like - * http://www.url.com?id=2380 - */ -static const char *find_host_sep(const char *url) -{ - const char *sep; - const char *query; - - /* Find the start of the hostname */ - sep = strstr(url, "//"); - if(!sep) - sep = url; - else - sep += 2; - - query = strchr(sep, '?'); - sep = strchr(sep, '/'); - - if(!sep) - sep = url + strlen(url); - - if(!query) - query = url + strlen(url); - - return sep < query ? sep : query; -} - -/* - * Decide in an encoding-independent manner whether a character in an - * URL must be escaped. The same criterion must be used in strlen_url() - * and strcpy_url(). - */ -static bool urlchar_needs_escaping(int c) -{ - return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c)); -} - -/* - * strlen_url() returns the length of the given URL if the spaces within the - * URL were properly URL encoded. - * URL encoding should be skipped for host names, otherwise IDN resolution - * will fail. - */ -static size_t strlen_url(const char *url, bool relative) -{ - const unsigned char *ptr; - size_t newlen = 0; - bool left = TRUE; /* left side of the ? */ - const unsigned char *host_sep = (const unsigned char *) url; - - if(!relative) - host_sep = (const unsigned char *) find_host_sep(url); - - for(ptr = (unsigned char *)url; *ptr; ptr++) { - - if(ptr < host_sep) { - ++newlen; - continue; - } - - switch(*ptr) { - case '?': - left = FALSE; - /* FALLTHROUGH */ - default: - if(urlchar_needs_escaping(*ptr)) - newlen += 2; - newlen++; - break; - case ' ': - if(left) - newlen += 3; - else - newlen++; - break; - } - } - return newlen; -} - -/* strcpy_url() copies a url to a output buffer and URL-encodes the spaces in - * the source URL accordingly. - * URL encoding should be skipped for host names, otherwise IDN resolution - * will fail. - */ -static void strcpy_url(char *output, const char *url, bool relative) -{ - /* we must add this with whitespace-replacing */ - bool left = TRUE; - const unsigned char *iptr; - char *optr = output; - const unsigned char *host_sep = (const unsigned char *) url; - - if(!relative) - host_sep = (const unsigned char *) find_host_sep(url); - - for(iptr = (unsigned char *)url; /* read from here */ - *iptr; /* until zero byte */ - iptr++) { - - if(iptr < host_sep) { - *optr++ = *iptr; - continue; - } - - switch(*iptr) { - case '?': - left = FALSE; - /* FALLTHROUGH */ - default: - if(urlchar_needs_escaping(*iptr)) { - snprintf(optr, 4, "%%%02x", *iptr); - optr += 3; - } - else - *optr++=*iptr; - break; - case ' ': - if(left) { - *optr++='%'; /* add a '%' */ - *optr++='2'; /* add a '2' */ - *optr++='0'; /* add a '0' */ - } - else - *optr++='+'; /* add a '+' here */ - break; - } - } - *optr = 0; /* zero terminate output buffer */ - -} - -/* - * Returns true if the given URL is absolute (as opposed to relative) - */ -static bool is_absolute_url(const char *url) -{ - char prot[16]; /* URL protocol string storage */ - char letter; /* used for a silly sscanf */ - - return (2 == sscanf(url, "%15[^?&/:]://%c", prot, &letter)) ? TRUE : FALSE; -} - -/* - * Concatenate a relative URL to a base URL making it absolute. - * URL-encodes any spaces. - * The returned pointer must be freed by the caller unless NULL - * (returns NULL on out of memory). - */ -static char *concat_url(const char *base, const char *relurl) -{ - /*** - TRY to append this new path to the old URL - to the right of the host part. Oh crap, this is doomed to cause - problems in the future... - */ - char *newest; - char *protsep; - char *pathsep; - size_t newlen; - bool host_changed = FALSE; - - const char *useurl = relurl; - size_t urllen; - - /* we must make our own copy of the URL to play with, as it may - point to read-only data */ - char *url_clone = strdup(base); - - if(!url_clone) - return NULL; /* skip out of this NOW */ - - /* protsep points to the start of the host name */ - protsep = strstr(url_clone, "//"); - if(!protsep) - protsep = url_clone; - else - protsep += 2; /* pass the slashes */ - - if('/' != relurl[0]) { - int level = 0; - - /* First we need to find out if there's a ?-letter in the URL, - and cut it and the right-side of that off */ - pathsep = strchr(protsep, '?'); - if(pathsep) - *pathsep = 0; - - /* we have a relative path to append to the last slash if there's one - available, or if the new URL is just a query string (starts with a - '?') we append the new one at the end of the entire currently worked - out URL */ - if(useurl[0] != '?') { - pathsep = strrchr(protsep, '/'); - if(pathsep) - *pathsep = 0; - } - - /* Check if there's any slash after the host name, and if so, remember - that position instead */ - pathsep = strchr(protsep, '/'); - if(pathsep) - protsep = pathsep + 1; - else - protsep = NULL; - - /* now deal with one "./" or any amount of "../" in the newurl - and act accordingly */ - - if((useurl[0] == '.') && (useurl[1] == '/')) - useurl += 2; /* just skip the "./" */ - - while((useurl[0] == '.') && - (useurl[1] == '.') && - (useurl[2] == '/')) { - level++; - useurl += 3; /* pass the "../" */ - } - - if(protsep) { - while(level--) { - /* cut off one more level from the right of the original URL */ - pathsep = strrchr(protsep, '/'); - if(pathsep) - *pathsep = 0; - else { - *protsep = 0; - break; - } - } - } - } - else { - /* We got a new absolute path for this server */ - - if((relurl[0] == '/') && (relurl[1] == '/')) { - /* the new URL starts with //, just keep the protocol part from the - original one */ - *protsep = 0; - useurl = &relurl[2]; /* we keep the slashes from the original, so we - skip the new ones */ - host_changed = TRUE; - } - else { - /* cut off the original URL from the first slash, or deal with URLs - without slash */ - pathsep = strchr(protsep, '/'); - if(pathsep) { - /* When people use badly formatted URLs, such as - "http://www.url.com?dir=/home/daniel" we must not use the first - slash, if there's a ?-letter before it! */ - char *sep = strchr(protsep, '?'); - if(sep && (sep < pathsep)) - pathsep = sep; - *pathsep = 0; - } - else { - /* There was no slash. Now, since we might be operating on a badly - formatted URL, such as "http://www.url.com?id=2380" which doesn't - use a slash separator as it is supposed to, we need to check for a - ?-letter as well! */ - pathsep = strchr(protsep, '?'); - if(pathsep) - *pathsep = 0; - } - } - } - - /* If the new part contains a space, this is a mighty stupid redirect - but we still make an effort to do "right". To the left of a '?' - letter we replace each space with %20 while it is replaced with '+' - on the right side of the '?' letter. - */ - newlen = strlen_url(useurl, !host_changed); - - urllen = strlen(url_clone); - - newest = malloc(urllen + 1 + /* possible slash */ - newlen + 1 /* zero byte */); - - if(!newest) { - free(url_clone); /* don't leak this */ - return NULL; - } - - /* copy over the root url part */ - memcpy(newest, url_clone, urllen); - - /* check if we need to append a slash */ - if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0])) - ; - else - newest[urllen++]='/'; - - /* then append the new piece on the right side */ - strcpy_url(&newest[urllen], useurl, !host_changed); - - free(url_clone); - - return newest; -} -#endif /* CURL_DISABLE_HTTP */ - /* * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string * as given by the remote server and set up the new URL to request. + * + * This function DOES NOT FREE the given url. */ CURLcode Curl_follow(struct Curl_easy *data, char *newurl, /* the Location: string */ @@ -1777,6 +1476,7 @@ CURLcode Curl_follow(struct Curl_easy *data, /* Location: redirect */ bool disallowport = FALSE; bool reachedmax = FALSE; + CURLUcode uc; if(type == FOLLOW_REDIR) { if((data->set.maxredirs != -1) && @@ -1809,33 +1509,19 @@ CURLcode Curl_follow(struct Curl_easy *data, } } - if(!is_absolute_url(newurl)) { - /*** - *DANG* this is an RFC 2068 violation. The URL is supposed - to be absolute and this doesn't seem to be that! - */ - char *absolute = concat_url(data->change.url, newurl); - if(!absolute) - return CURLE_OUT_OF_MEMORY; - newurl = absolute; - } - else { - /* The new URL MAY contain space or high byte values, that means a mighty - stupid redirect URL but we still make an effort to do "right". */ - char *newest; - size_t newlen = strlen_url(newurl, FALSE); - + if(Curl_is_absolute_url(newurl, NULL, MAX_SCHEME_LEN)) /* This is an absolute URL, don't allow the custom port number */ disallowport = TRUE; - newest = malloc(newlen + 1); /* get memory for this */ - if(!newest) - return CURLE_OUT_OF_MEMORY; - - strcpy_url(newest, newurl, FALSE); /* create a space-free URL */ - newurl = newest; /* use this instead now */ + DEBUGASSERT(data->state.uh); + uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, + (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : 0); + if(uc) + return Curl_uc_to_curlcode(uc); - } + uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); + if(uc) + return Curl_uc_to_curlcode(uc); if(type == FOLLOW_FAKE) { /* we're only figuring out the new url if we would've followed locations @@ -1852,10 +1538,8 @@ CURLcode Curl_follow(struct Curl_easy *data, if(disallowport) data->state.allow_port = FALSE; - if(data->change.url_alloc) { + if(data->change.url_alloc) Curl_safefree(data->change.url); - data->change.url_alloc = FALSE; - } data->change.url = newurl; data->change.url_alloc = TRUE; @@ -2021,8 +1705,13 @@ CURLcode Curl_retry_request(struct connectdata *conn, if(conn->handler->protocol&PROTO_FAMILY_HTTP) { struct HTTP *http = data->req.protop; - if(http->writebytecount) - return Curl_readrewind(conn); + if(http->writebytecount) { + CURLcode result = Curl_readrewind(conn); + if(result) { + Curl_safefree(*url); + return result; + } + } } } return CURLE_OK; diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h index 9263e5b..9742455 100644 --- a/Utilities/cmcurl/lib/transfer.h +++ b/Utilities/cmcurl/lib/transfer.h @@ -71,4 +71,3 @@ Curl_setup_transfer (struct connectdata *data, ); #endif /* HEADER_CURL_TRANSFER_H */ - diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index f159008..0d5a13f 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -92,6 +92,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "non-ascii.h" #include "inet_pton.h" #include "getinfo.h" +#include "urlapi-int.h" /* And now for the protocols */ #include "ftp.h" @@ -127,10 +128,6 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); static void conn_free(struct connectdata *conn); static void free_fixed_hostname(struct hostname *host); -static CURLcode parse_url_login(struct Curl_easy *data, - struct connectdata *conn, - char **userptr, char **passwdptr, - char **optionsptr); static unsigned int get_protocol_family(unsigned int protocol); /* Some parts of the code (e.g. chunked encoding) assume this buffer has at @@ -294,6 +291,22 @@ void Curl_freeset(struct Curl_easy *data) Curl_mime_cleanpart(&data->set.mimepost); } +/* free the URL pieces */ +void Curl_up_free(struct Curl_easy *data) +{ + struct urlpieces *up = &data->state.up; + Curl_safefree(up->scheme); + Curl_safefree(up->hostname); + Curl_safefree(up->port); + Curl_safefree(up->user); + Curl_safefree(up->password); + Curl_safefree(up->options); + Curl_safefree(up->path); + Curl_safefree(up->query); + curl_url_cleanup(data->state.uh); + data->state.uh = NULL; +} + /* * This is the internal function curl_easy_cleanup() calls. This should * cleanup and free all resources associated with this sessionhandle. @@ -313,16 +326,17 @@ CURLcode Curl_close(struct Curl_easy *data) Curl_expire_clear(data); /* shut off timers */ m = data->multi; - if(m) /* This handle is still part of a multi handle, take care of this first and detach this handle from there. */ curl_multi_remove_handle(data->multi, data); - if(data->multi_easy) + if(data->multi_easy) { /* when curl_easy_perform() is used, it creates its own multi handle to use and this is the one */ curl_multi_cleanup(data->multi_easy); + data->multi_easy = NULL; + } /* Destroy the timeout list that is held in the easy handle. It is /normally/ done by curl_multi_remove_handle() but this is "just in @@ -336,10 +350,6 @@ CURLcode Curl_close(struct Curl_easy *data) if(data->state.rangestringalloc) free(data->state.range); - /* Free the pathbuffer */ - Curl_safefree(data->state.pathbuffer); - data->state.path = NULL; - /* freed here just in case DONE wasn't called */ Curl_free_request_state(data); @@ -359,12 +369,7 @@ CURLcode Curl_close(struct Curl_easy *data) } data->change.referer = NULL; - if(data->change.url_alloc) { - Curl_safefree(data->change.url); - data->change.url_alloc = FALSE; - } - data->change.url = NULL; - + Curl_up_free(data); Curl_safefree(data->state.buffer); Curl_safefree(data->state.headerbuff); Curl_safefree(data->state.ulbuf); @@ -516,25 +521,28 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->wildcard_enabled = FALSE; set->chunk_bgn = ZERO_NULL; set->chunk_end = ZERO_NULL; - - /* tcp keepalives are disabled by default, but provide reasonable values for - * the interval and idle times. - */ set->tcp_keepalive = FALSE; set->tcp_keepintvl = 60; set->tcp_keepidle = 60; set->tcp_fastopen = FALSE; set->tcp_nodelay = TRUE; - set->ssl_enable_npn = TRUE; set->ssl_enable_alpn = TRUE; - set->expect_100_timeout = 1000L; /* Wait for a second by default. */ set->sep_headers = TRUE; /* separated header lists by default */ set->buffer_size = READBUFFER_SIZE; - set->upload_buffer_size = UPLOAD_BUFSIZE; + set->upload_buffer_size = UPLOADBUFFER_DEFAULT; set->happy_eyeballs_timeout = CURL_HET_DEFAULT; - + set->fnmatch = ZERO_NULL; + set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; + set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ + set->httpversion = +#ifdef USE_NGHTTP2 + CURL_HTTP_VERSION_2TLS +#else + CURL_HTTP_VERSION_1_1 +#endif + ; Curl_http2_init_userset(set); return result; } @@ -594,8 +602,6 @@ CURLcode Curl_open(struct Curl_easy **curl) data->progress.flags |= PGRS_HIDE; data->state.current_speed = -1; /* init to negative == impossible */ - data->set.fnmatch = ZERO_NULL; - data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ Curl_http2_init_state(&data->state); } @@ -1831,6 +1837,12 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) /* Store creation time to help future close decision making */ conn->created = Curl_now(); + /* Store current time to give a baseline to keepalive connection times. */ + conn->keepalive = Curl_now(); + + /* Store off the configured connection upkeep time. */ + conn->upkeep_interval_ms = data->set.upkeep_interval_ms; + conn->data = data; /* Setup the association between this connection and the Curl_easy */ @@ -1937,30 +1949,37 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) return NULL; } -static CURLcode findprotocol(struct Curl_easy *data, - struct connectdata *conn, - const char *protostr) +/* returns the handler if the given scheme is built-in */ +const struct Curl_handler *Curl_builtin_scheme(const char *scheme) { const struct Curl_handler * const *pp; const struct Curl_handler *p; - - /* Scan protocol handler table and match against 'protostr' to set a few - variables based on the URL. Now that the handler may be changed later - when the protocol specific setup function is called. */ - for(pp = protocols; (p = *pp) != NULL; pp++) { - if(strcasecompare(p->scheme, protostr)) { + /* Scan protocol handler table and match against 'scheme'. The handler may + be changed later when the protocol specific setup function is called. */ + for(pp = protocols; (p = *pp) != NULL; pp++) + if(strcasecompare(p->scheme, scheme)) /* Protocol found in table. Check if allowed */ - if(!(data->set.allowed_protocols & p->protocol)) - /* nope, get out */ - break; + return p; + return NULL; /* not found */ +} + + +static CURLcode findprotocol(struct Curl_easy *data, + struct connectdata *conn, + const char *protostr) +{ + const struct Curl_handler *p = Curl_builtin_scheme(protostr); - /* it is allowed for "normal" request, now do an extra check if this is - the result of a redirect */ - if(data->state.this_is_a_follow && - !(data->set.redir_protocols & p->protocol)) - /* nope, get out */ - break; + if(p && /* Protocol found in table. Check if allowed */ + (data->set.allowed_protocols & p->protocol)) { + /* it is allowed for "normal" request, now do an extra check if this is + the result of a redirect */ + if(data->state.this_is_a_follow && + !(data->set.redir_protocols & p->protocol)) + /* nope, get out */ + ; + else { /* Perform setup complement if some. */ conn->handler = conn->given = p; @@ -1969,7 +1988,6 @@ static CURLcode findprotocol(struct Curl_easy *data, } } - /* The protocol was not found in the table, but we don't have to assign it to anything since it is already assigned to a dummy-struct in the create_conn() function when the connectdata struct is allocated. */ @@ -1979,379 +1997,134 @@ static CURLcode findprotocol(struct Curl_easy *data, return CURLE_UNSUPPORTED_PROTOCOL; } + +CURLcode Curl_uc_to_curlcode(CURLUcode uc) +{ + switch(uc) { + default: + return CURLE_URL_MALFORMAT; + case CURLUE_UNSUPPORTED_SCHEME: + return CURLE_UNSUPPORTED_PROTOCOL; + case CURLUE_OUT_OF_MEMORY: + return CURLE_OUT_OF_MEMORY; + case CURLUE_USER_NOT_ALLOWED: + return CURLE_LOGIN_DENIED; + } +} + /* * Parse URL and fill in the relevant members of the connection struct. */ static CURLcode parseurlandfillconn(struct Curl_easy *data, - struct connectdata *conn, - bool *prot_missing, - char **userp, char **passwdp, - char **optionsp) + struct connectdata *conn) { - char *at; - char *fragment; - char *path = data->state.path; - char *query; - int rc; - const char *protop = ""; CURLcode result; - bool rebuild_url = FALSE; - bool url_has_scheme = FALSE; - char protobuf[16]; - - *prot_missing = FALSE; + CURLU *uh; + CURLUcode uc; + char *hostname; - /* We might pass the entire URL into the request so we need to make sure - * there are no bad characters in there.*/ - if(strpbrk(data->change.url, "\r\n")) { - failf(data, "Illegal characters found in URL"); - return CURLE_URL_MALFORMAT; - } + Curl_up_free(data); /* cleanup previous leftovers first */ - /************************************************************* - * Parse the URL. - * - * We need to parse the url even when using the proxy, because we will need - * the hostname and port in case we are trying to SSL connect through the - * proxy -- and we don't know if we will need to use SSL until we parse the - * url ... - ************************************************************/ - if(data->change.url[0] == ':') { - failf(data, "Bad URL, colon is first character"); - return CURLE_URL_MALFORMAT; - } + /* parse the URL */ + uh = data->state.uh = curl_url(); + if(!uh) + return CURLE_OUT_OF_MEMORY; - /* MSDOS/Windows style drive prefix, eg c: in c:foo */ -#define STARTS_WITH_DRIVE_PREFIX(str) \ - ((('a' <= str[0] && str[0] <= 'z') || \ - ('A' <= str[0] && str[0] <= 'Z')) && \ - (str[1] == ':')) - - /* MSDOS/Windows style drive prefix, optionally with - * a '|' instead of ':', followed by a slash or NUL */ -#define STARTS_WITH_URL_DRIVE_PREFIX(str) \ - ((('a' <= (str)[0] && (str)[0] <= 'z') || \ - ('A' <= (str)[0] && (str)[0] <= 'Z')) && \ - ((str)[1] == ':' || (str)[1] == '|') && \ - ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0)) - - /* Don't mistake a drive letter for a scheme if the default protocol is file. - curld --proto-default file c:/foo/bar.txt */ - if(STARTS_WITH_DRIVE_PREFIX(data->change.url) && - data->set.str[STRING_DEFAULT_PROTOCOL] && - strcasecompare(data->set.str[STRING_DEFAULT_PROTOCOL], "file")) { - ; /* do nothing */ - } - else { /* check for a scheme */ - int i; - for(i = 0; i < 16 && data->change.url[i]; ++i) { - if(data->change.url[i] == '/') - break; - if(data->change.url[i] == ':') { - url_has_scheme = TRUE; - break; - } - } + if(data->set.str[STRING_DEFAULT_PROTOCOL] && + !Curl_is_absolute_url(data->change.url, NULL, MAX_SCHEME_LEN)) { + char *url; + if(data->change.url_alloc) + free(data->change.url); + url = aprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL], + data->change.url); + if(!url) + return CURLE_OUT_OF_MEMORY; + data->change.url = url; + data->change.url_alloc = TRUE; } - /* handle the file: scheme */ - if((url_has_scheme && strncasecompare(data->change.url, "file:", 5)) || - (!url_has_scheme && data->set.str[STRING_DEFAULT_PROTOCOL] && - strcasecompare(data->set.str[STRING_DEFAULT_PROTOCOL], "file"))) { - if(url_has_scheme) - rc = sscanf(data->change.url, "%*15[^\n/:]:%[^\n]", path); - else - rc = sscanf(data->change.url, "%[^\n]", path); - - if(rc != 1) { - failf(data, "Bad URL"); - return CURLE_URL_MALFORMAT; - } + uc = curl_url_set(uh, CURLUPART_URL, data->change.url, + CURLU_GUESS_SCHEME | + CURLU_NON_SUPPORT_SCHEME | + (data->set.disallow_username_in_url ? + CURLU_DISALLOW_USER : 0) | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)); + if(uc) + return Curl_uc_to_curlcode(uc); - /* Extra handling URLs with an authority component (i.e. that start with - * "file://") - * - * We allow omitted hostname (e.g. file:/<path>) -- valid according to - * RFC 8089, but not the (current) WHAT-WG URL spec. - */ - if(url_has_scheme && path[0] == '/' && path[1] == '/') { - /* swallow the two slashes */ - char *ptr = &path[2]; + uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); + if(uc) + return Curl_uc_to_curlcode(uc); - /* - * According to RFC 8089, a file: URL can be reliably dereferenced if: - * - * o it has no/blank hostname, or - * - * o the hostname matches "localhost" (case-insensitively), or - * - * o the hostname is a FQDN that resolves to this machine. - * - * For brevity, we only consider URLs with empty, "localhost", or - * "127.0.0.1" hostnames as local. - * - * Additionally, there is an exception for URLs with a Windows drive - * letter in the authority (which was accidentally omitted from RFC 8089 - * Appendix E, but believe me, it was meant to be there. --MK) - */ - if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { - /* the URL includes a host name, it must match "localhost" or - "127.0.0.1" to be valid */ - if(!checkprefix("localhost/", ptr) && - !checkprefix("127.0.0.1/", ptr)) { - failf(data, "Invalid file://hostname/, " - "expected localhost or 127.0.0.1 or none"); - return CURLE_URL_MALFORMAT; - } - ptr += 9; /* now points to the slash after the host */ - } - - /* This cannot be done with strcpy, as the memory chunks overlap! */ - memmove(path, ptr, strlen(ptr) + 1); - } - -#if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) - /* Don't allow Windows drive letters when not in Windows. - * This catches both "file:/c:" and "file:c:" */ - if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || - STARTS_WITH_URL_DRIVE_PREFIX(path)) { - failf(data, "File drive letters are only accepted in MSDOS/Windows."); - return CURLE_URL_MALFORMAT; - } -#else - /* If the path starts with a slash and a drive letter, ditch the slash */ - if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { - /* This cannot be done with strcpy, as the memory chunks overlap! */ - memmove(path, &path[1], strlen(&path[1]) + 1); - } -#endif + result = findprotocol(data, conn, data->state.up.scheme); + if(result) + return result; - protop = "file"; /* protocol string */ - *prot_missing = !url_has_scheme; + uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, + CURLU_URLDECODE); + if(!uc) { + conn->user = strdup(data->state.up.user); + if(!conn->user) + return CURLE_OUT_OF_MEMORY; + conn->bits.user_passwd = TRUE; } - else { - /* clear path */ - char slashbuf[4]; - path[0] = 0; - - rc = sscanf(data->change.url, - "%15[^\n/:]:%3[/]%[^\n/?#]%[^\n]", - protobuf, slashbuf, conn->host.name, path); - if(2 == rc) { - failf(data, "Bad URL"); - return CURLE_URL_MALFORMAT; - } - if(3 > rc) { - - /* - * The URL was badly formatted, let's try the browser-style _without_ - * protocol specified like 'http://'. - */ - rc = sscanf(data->change.url, "%[^\n/?#]%[^\n]", conn->host.name, path); - if(1 > rc) { - /* - * We couldn't even get this format. - * djgpp 2.04 has a sscanf() bug where 'conn->host.name' is - * assigned, but the return value is EOF! - */ -#if defined(__DJGPP__) && (DJGPP_MINOR == 4) - if(!(rc == -1 && *conn->host.name)) -#endif - { - failf(data, "<url> malformed"); - return CURLE_URL_MALFORMAT; - } - } - - /* - * Since there was no protocol part specified in the URL use the - * user-specified default protocol. If we weren't given a default make a - * guess by matching some protocols against the host's outermost - * sub-domain name. Finally if there was no match use HTTP. - */ + else if(uc != CURLUE_NO_USER) + return Curl_uc_to_curlcode(uc); - protop = data->set.str[STRING_DEFAULT_PROTOCOL]; - if(!protop) { - /* Note: if you add a new protocol, please update the list in - * lib/version.c too! */ - if(checkprefix("FTP.", conn->host.name)) - protop = "ftp"; - else if(checkprefix("DICT.", conn->host.name)) - protop = "DICT"; - else if(checkprefix("LDAP.", conn->host.name)) - protop = "LDAP"; - else if(checkprefix("IMAP.", conn->host.name)) - protop = "IMAP"; - else if(checkprefix("SMTP.", conn->host.name)) - protop = "smtp"; - else if(checkprefix("POP3.", conn->host.name)) - protop = "pop3"; - else - protop = "http"; - } - - *prot_missing = TRUE; /* not given in URL */ - } - else { - size_t s = strlen(slashbuf); - protop = protobuf; - if(s != 2) { - infof(data, "Unwillingly accepted illegal URL using %zu slash%s!\n", - s, s>1?"es":""); - - if(data->change.url_alloc) - free(data->change.url); - /* repair the URL to use two slashes */ - data->change.url = aprintf("%s://%s%s", - protobuf, conn->host.name, path); - if(!data->change.url) - return CURLE_OUT_OF_MEMORY; - data->change.url_alloc = TRUE; - } - } + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, + CURLU_URLDECODE); + if(!uc) { + conn->passwd = strdup(data->state.up.password); + if(!conn->passwd) + return CURLE_OUT_OF_MEMORY; + conn->bits.user_passwd = TRUE; } + else if(uc != CURLUE_NO_PASSWORD) + return Curl_uc_to_curlcode(uc); - /* We search for '?' in the host name (but only on the right side of a - * @-letter to allow ?-letters in username and password) to handle things - * like http://example.com?param= (notice the missing '/'). - */ - at = strchr(conn->host.name, '@'); - if(at) - query = strchr(at + 1, '?'); - else - query = strchr(conn->host.name, '?'); - - if(query) { - /* We must insert a slash before the '?'-letter in the URL. If the URL had - a slash after the '?', that is where the path currently begins and the - '?string' is still part of the host name. - - We must move the trailing part from the host name and put it first in - the path. And have it all prefixed with a slash. - */ - - size_t hostlen = strlen(query); - size_t pathlen = strlen(path); - - /* move the existing path plus the zero byte forward, to make room for - the host-name part */ - memmove(path + hostlen + 1, path, pathlen + 1); - - /* now copy the trailing host part in front of the existing path */ - memcpy(path + 1, query, hostlen); - - path[0]='/'; /* prepend the missing slash */ - rebuild_url = TRUE; - - *query = 0; /* now cut off the hostname at the ? */ - } - else if(!path[0]) { - /* if there's no path set, use a single slash */ - strcpy(path, "/"); - rebuild_url = TRUE; + uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, + CURLU_URLDECODE); + if(!uc) { + conn->options = strdup(data->state.up.options); + if(!conn->options) + return CURLE_OUT_OF_MEMORY; } + else if(uc != CURLUE_NO_OPTIONS) + return Curl_uc_to_curlcode(uc); - /* If the URL is malformatted (missing a '/' after hostname before path) we - * insert a slash here. The only letters except '/' that can start a path is - * '?' and '#' - as controlled by the two sscanf() patterns above. - */ - if(path[0] != '/') { - /* We need this function to deal with overlapping memory areas. We know - that the memory area 'path' points to is 'urllen' bytes big and that - is bigger than the path. Use +1 to move the zero byte too. */ - memmove(&path[1], path, strlen(path) + 1); - path[0] = '/'; - rebuild_url = TRUE; - } - else if(!data->set.path_as_is) { - /* sanitise paths and remove ../ and ./ sequences according to RFC3986 */ - char *newp = Curl_dedotdotify(path); - if(!newp) + uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); + if(uc) { + if(!strcasecompare("file", data->state.up.scheme)) return CURLE_OUT_OF_MEMORY; - - if(strcmp(newp, path)) { - rebuild_url = TRUE; - free(data->state.pathbuffer); - data->state.pathbuffer = newp; - data->state.path = newp; - path = newp; - } - else - free(newp); } - /* - * "rebuild_url" means that one or more URL components have been modified so - * we need to generate an updated full version. We need the corrected URL - * when communicating over HTTP proxy and we don't know at this point if - * we're using a proxy or not. - */ - if(rebuild_url) { - char *reurl; - - size_t plen = strlen(path); /* new path, should be 1 byte longer than - the original */ - size_t prefixlen = strlen(conn->host.name); - - if(!*prot_missing) { - size_t protolen = strlen(protop); - - if(curl_strnequal(protop, data->change.url, protolen)) - prefixlen += protolen; - else { - failf(data, "<url> malformed"); - return CURLE_URL_MALFORMAT; - } - - if(curl_strnequal("://", &data->change.url[protolen], 3)) - prefixlen += 3; - /* only file: is allowed to omit one or both slashes */ - else if(curl_strnequal("file:", data->change.url, 5)) - prefixlen += 1 + (data->change.url[5] == '/'); - else { - failf(data, "<url> malformed"); - return CURLE_URL_MALFORMAT; - } - } + uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, 0); + if(uc) + return Curl_uc_to_curlcode(uc); - reurl = malloc(prefixlen + plen + 1); - if(!reurl) + uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port, + CURLU_DEFAULT_PORT); + if(uc) { + if(!strcasecompare("file", data->state.up.scheme)) return CURLE_OUT_OF_MEMORY; - - /* copy the prefix */ - memcpy(reurl, data->change.url, prefixlen); - - /* append the trailing piece + zerobyte */ - memcpy(&reurl[prefixlen], path, plen + 1); - - /* possible free the old one */ - if(data->change.url_alloc) { - Curl_safefree(data->change.url); - data->change.url_alloc = FALSE; - } - - infof(data, "Rebuilt URL to: %s\n", reurl); - - data->change.url = reurl; - data->change.url_alloc = TRUE; /* free this later */ + } + else { + unsigned long port = strtoul(data->state.up.port, NULL, 10); + conn->remote_port = curlx_ultous(port); } - result = findprotocol(data, conn, protop); - if(result) - return result; + (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); - /* - * Parse the login details from the URL and strip them out of - * the host name - */ - result = parse_url_login(data, conn, userp, passwdp, optionsp); - if(result) - return result; + hostname = data->state.up.hostname; + if(!hostname) + /* this is for file:// transfers, get a dummy made */ + hostname = (char *)""; - if(conn->host.name[0] == '[') { + if(hostname[0] == '[') { /* This looks like an IPv6 address literal. See if there is an address - scope if there is no location header */ - char *percent = strchr(conn->host.name, '%'); + scope. */ + char *percent = strchr(++hostname, '%'); + conn->bits.ipv6_ip = TRUE; if(percent) { unsigned int identifier_offset = 3; char *endp; @@ -2399,33 +2172,22 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, infof(data, "Invalid IPv6 address format\n"); } } + percent = strchr(hostname, ']'); + if(percent) + /* terminate IPv6 numerical at end bracket */ + *percent = 0; } + /* make sure the connect struct gets its own copy of the host name */ + conn->host.rawalloc = strdup(hostname); + if(!conn->host.rawalloc) + return CURLE_OUT_OF_MEMORY; + conn->host.name = conn->host.rawalloc; + if(data->set.scope_id) /* Override any scope that was set above. */ conn->scope_id = data->set.scope_id; - /* Remove the fragment part of the path. Per RFC 2396, this is always the - last part of the URI. We are looking for the first '#' so that we deal - gracefully with non conformant URI such as http://example.com#foo#bar. */ - fragment = strchr(path, '#'); - if(fragment) { - *fragment = 0; - - /* we know the path part ended with a fragment, so we know the full URL - string does too and we need to cut it off from there so it isn't used - over proxy */ - fragment = strchr(data->change.url, '#'); - if(fragment) - *fragment = 0; - } - - /* - * So if the URL was A://B/C#D, - * protop is A - * conn->host.name is B - * data->state.path is /C - */ return CURLE_OK; } @@ -2540,11 +2302,8 @@ static bool check_noproxy(const char *name, const char *no_proxy) if(!endptr) return FALSE; name++; - } - else - endptr = strchr(name, ':'); - if(endptr) namelen = endptr - name; + } else namelen = strlen(name); @@ -3077,131 +2836,6 @@ out: #endif /* CURL_DISABLE_PROXY */ /* - * parse_url_login() - * - * Parse the login details (user name, password and options) from the URL and - * strip them out of the host name - * - * Inputs: data->set.use_netrc (CURLOPT_NETRC) - * conn->host.name - * - * Outputs: (almost :- all currently undefined) - * conn->bits.user_passwd - non-zero if non-default passwords exist - * user - non-zero length if defined - * passwd - non-zero length if defined - * options - non-zero length if defined - * conn->host.name - remove user name and password - */ -static CURLcode parse_url_login(struct Curl_easy *data, - struct connectdata *conn, - char **user, char **passwd, char **options) -{ - CURLcode result = CURLE_OK; - char *userp = NULL; - char *passwdp = NULL; - char *optionsp = NULL; - - /* At this point, we're hoping all the other special cases have - * been taken care of, so conn->host.name is at most - * [user[:password][;options]]@]hostname - * - * We need somewhere to put the embedded details, so do that first. - */ - - char *ptr = strchr(conn->host.name, '@'); - char *login = conn->host.name; - - DEBUGASSERT(!**user); - DEBUGASSERT(!**passwd); - DEBUGASSERT(!**options); - DEBUGASSERT(conn->handler); - - if(!ptr) - goto out; - - /* We will now try to extract the - * possible login information in a string like: - * ftp://user:password@ftp.my.site:8021/README */ - conn->host.name = ++ptr; - - /* So the hostname is sane. Only bother interpreting the - * results if we could care. It could still be wasted - * work because it might be overtaken by the programmatically - * set user/passwd, but doing that first adds more cases here :-( - */ - - if(data->set.use_netrc == CURL_NETRC_REQUIRED) - goto out; - - /* We could use the login information in the URL so extract it. Only parse - options if the handler says we should. */ - result = - Curl_parse_login_details(login, ptr - login - 1, - &userp, &passwdp, - (conn->handler->flags & PROTOPT_URLOPTIONS)? - &optionsp:NULL); - if(result) - goto out; - - if(userp) { - char *newname; - - if(data->set.disallow_username_in_url) { - failf(data, "Option DISALLOW_USERNAME_IN_URL is set " - "and url contains username."); - result = CURLE_LOGIN_DENIED; - goto out; - } - - /* We have a user in the URL */ - conn->bits.userpwd_in_url = TRUE; - conn->bits.user_passwd = TRUE; /* enable user+password */ - - /* Decode the user */ - result = Curl_urldecode(data, userp, 0, &newname, NULL, FALSE); - if(result) { - goto out; - } - - free(*user); - *user = newname; - } - - if(passwdp) { - /* We have a password in the URL so decode it */ - char *newpasswd; - result = Curl_urldecode(data, passwdp, 0, &newpasswd, NULL, FALSE); - if(result) { - goto out; - } - - free(*passwd); - *passwd = newpasswd; - } - - if(optionsp) { - /* We have an options list in the URL so decode it */ - char *newoptions; - result = Curl_urldecode(data, optionsp, 0, &newoptions, NULL, FALSE); - if(result) { - goto out; - } - - free(*options); - *options = newoptions; - } - - - out: - - free(userp); - free(passwdp); - free(optionsp); - - return result; -} - -/* * Curl_parse_login_details() * * This is used to parse a login string for user name, password and options in @@ -3223,7 +2857,7 @@ static CURLcode parse_url_login(struct Curl_easy *data, * len [in] - The length of the login string. * userp [in/out] - The address where a pointer to newly allocated memory * holding the user will be stored upon completion. - * passdwp [in/out] - The address where a pointer to newly allocated memory + * passwdp [in/out] - The address where a pointer to newly allocated memory * holding the password will be stored upon completion. * optionsp [in/out] - The address where a pointer to newly allocated memory * holding the options will be stored upon completion. @@ -3334,131 +2968,23 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, * No matter if we use a proxy or not, we have to figure out the remote * port number of various reasons. * - * To be able to detect port number flawlessly, we must not confuse them - * IPv6-specified addresses in the [0::1] style. (RFC2732) - * - * The conn->host.name is currently [user:passwd@]host[:port] where host - * could be a hostname, IPv4 address or IPv6 address. - * * The port number embedded in the URL is replaced, if necessary. *************************************************************/ static CURLcode parse_remote_port(struct Curl_easy *data, struct connectdata *conn) { - char *portptr; - char endbracket; - - /* Note that at this point, the IPv6 address cannot contain any scope - suffix as that has already been removed in the parseurlandfillconn() - function */ - if((1 == sscanf(conn->host.name, "[%*45[0123456789abcdefABCDEF:.]%c", - &endbracket)) && - (']' == endbracket)) { - /* this is a RFC2732-style specified IP-address */ - conn->bits.ipv6_ip = TRUE; - - conn->host.name++; /* skip over the starting bracket */ - portptr = strchr(conn->host.name, ']'); - if(portptr) { - *portptr++ = '\0'; /* zero terminate, killing the bracket */ - if(*portptr) { - if (*portptr != ':') { - failf(data, "IPv6 closing bracket followed by '%c'", *portptr); - return CURLE_URL_MALFORMAT; - } - } - else - portptr = NULL; /* no port number available */ - } - } - else { -#ifdef ENABLE_IPV6 - struct in6_addr in6; - if(Curl_inet_pton(AF_INET6, conn->host.name, &in6) > 0) { - /* This is a numerical IPv6 address, meaning this is a wrongly formatted - URL */ - failf(data, "IPv6 numerical address used in URL without brackets"); - return CURLE_URL_MALFORMAT; - } -#endif - - portptr = strchr(conn->host.name, ':'); - } if(data->set.use_port && data->state.allow_port) { - /* if set, we use this and ignore the port possibly given in the URL */ + /* if set, we use this instead of the port possibly given in the URL */ + char portbuf[16]; + CURLUcode uc; conn->remote_port = (unsigned short)data->set.use_port; - if(portptr) - *portptr = '\0'; /* cut off the name there anyway - if there was a port - number - since the port number is to be ignored! */ - if(conn->bits.httpproxy) { - /* we need to create new URL with the new port number */ - char *url; - char type[12]=""; - - if(conn->bits.type_set) - snprintf(type, sizeof(type), ";type=%c", - data->set.prefer_ascii?'A': - (data->set.ftp_list_only?'D':'I')); - - /* - * This synthesized URL isn't always right--suffixes like ;type=A are - * stripped off. It would be better to work directly from the original - * URL and simply replace the port part of it. - */ - url = aprintf("%s://%s%s%s:%d%s%s%s", conn->given->scheme, - conn->bits.ipv6_ip?"[":"", conn->host.name, - conn->bits.ipv6_ip?"]":"", conn->remote_port, - data->state.slash_removed?"/":"", data->state.path, - type); - if(!url) - return CURLE_OUT_OF_MEMORY; - - if(data->change.url_alloc) { - Curl_safefree(data->change.url); - data->change.url_alloc = FALSE; - } - - data->change.url = url; - data->change.url_alloc = TRUE; - } - } - else if(portptr) { - /* no CURLOPT_PORT given, extract the one from the URL */ - - char *rest; - long port; - - port = strtol(portptr + 1, &rest, 10); /* Port number must be decimal */ - - if((port < 0) || (port > 0xffff)) { - /* Single unix standard says port numbers are 16 bits long */ - failf(data, "Port number out of range"); - return CURLE_URL_MALFORMAT; - } - - if(rest[0]) { - failf(data, "Port number ended with '%c'", rest[0]); - return CURLE_URL_MALFORMAT; - } - - if(rest != &portptr[1]) { - *portptr = '\0'; /* cut off the name there */ - conn->remote_port = curlx_ultous(port); - } - else { - /* Browser behavior adaptation. If there's a colon with no digits after, - just cut off the name there which makes us ignore the colon and just - use the default port. Firefox and Chrome both do that. */ - *portptr = '\0'; - } + snprintf(portbuf, sizeof(portbuf), "%u", conn->remote_port); + uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0); + if(uc) + return CURLE_OUT_OF_MEMORY; } - /* only if remote_port was not already parsed off the URL we use the - default port number */ - if(conn->remote_port < 0) - conn->remote_port = (unsigned short)conn->given->defport; - return CURLE_OK; } @@ -3470,11 +2996,16 @@ static CURLcode override_login(struct Curl_easy *data, struct connectdata *conn, char **userp, char **passwdp, char **optionsp) { + bool user_changed = FALSE; + bool passwd_changed = FALSE; + CURLUcode uc; if(data->set.str[STRING_USERNAME]) { free(*userp); *userp = strdup(data->set.str[STRING_USERNAME]); if(!*userp) return CURLE_OUT_OF_MEMORY; + conn->bits.user_passwd = TRUE; /* enable user+password */ + user_changed = TRUE; } if(data->set.str[STRING_PASSWORD]) { @@ -3482,6 +3013,8 @@ static CURLcode override_login(struct Curl_easy *data, *passwdp = strdup(data->set.str[STRING_PASSWORD]); if(!*passwdp) return CURLE_OUT_OF_MEMORY; + conn->bits.user_passwd = TRUE; /* enable user+password */ + passwd_changed = TRUE; } if(data->set.str[STRING_OPTIONS]) { @@ -3493,9 +3026,16 @@ static CURLcode override_login(struct Curl_easy *data, conn->bits.netrc = FALSE; if(data->set.use_netrc != CURL_NETRC_IGNORED) { - int ret = Curl_parsenetrc(conn->host.name, - userp, passwdp, - data->set.str[STRING_NETRC_FILE]); + char *nuser = NULL; + char *npasswd = NULL; + int ret; + + if(data->set.use_netrc == CURL_NETRC_OPTIONAL) + nuser = *userp; /* to separate otherwise identical machines */ + + ret = Curl_parsenetrc(conn->host.name, + &nuser, &npasswd, + 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", @@ -3509,55 +3049,85 @@ static CURLcode override_login(struct Curl_easy *data, file, so that it is safe to use even if we followed a Location: to a different host or similar. */ conn->bits.netrc = TRUE; - conn->bits.user_passwd = TRUE; /* enable user+password */ + + if(data->set.use_netrc == CURL_NETRC_OPTIONAL) { + /* prefer credentials outside netrc */ + if(nuser && !*userp) { + free(*userp); + *userp = nuser; + user_changed = TRUE; + } + if(npasswd && !*passwdp) { + free(*passwdp); + *passwdp = npasswd; + passwd_changed = TRUE; + } + } + else { + /* prefer netrc credentials */ + if(nuser) { + free(*userp); + *userp = nuser; + user_changed = TRUE; + } + if(npasswd) { + free(*passwdp); + *passwdp = npasswd; + passwd_changed = TRUE; + } + } } } + /* for updated strings, we update them in the URL */ + if(user_changed) { + uc = curl_url_set(data->state.uh, CURLUPART_USER, *userp, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + } + if(passwd_changed) { + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, *passwdp, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + } return CURLE_OK; } /* * Set the login details so they're available in the connection */ -static CURLcode set_login(struct connectdata *conn, - const char *user, const char *passwd, - const char *options) +static CURLcode set_login(struct connectdata *conn) { CURLcode result = CURLE_OK; + const char *setuser = CURL_DEFAULT_USER; + const char *setpasswd = CURL_DEFAULT_PASSWORD; /* If our protocol needs a password and we have none, use the defaults */ - if((conn->handler->flags & PROTOPT_NEEDSPWD) && !conn->bits.user_passwd) { - /* Store the default user */ - conn->user = strdup(CURL_DEFAULT_USER); - - /* Store the default password */ - if(conn->user) - conn->passwd = strdup(CURL_DEFAULT_PASSWORD); - else - conn->passwd = NULL; - - /* This is the default password, so DON'T set conn->bits.user_passwd */ - } + if((conn->handler->flags & PROTOPT_NEEDSPWD) && !conn->bits.user_passwd) + ; else { - /* Store the user, zero-length if not set */ - conn->user = strdup(user); - - /* Store the password (only if user is present), zero-length if not set */ - if(conn->user) - conn->passwd = strdup(passwd); - else - conn->passwd = NULL; + setuser = ""; + setpasswd = ""; + } + /* Store the default user */ + if(!conn->user) { + conn->user = strdup(setuser); + if(!conn->user) + return CURLE_OUT_OF_MEMORY; } - if(!conn->user || !conn->passwd) - result = CURLE_OUT_OF_MEMORY; - - /* Store the options, null if not set */ - if(!result && options[0]) { - conn->options = strdup(options); + /* Store the default password */ + if(!conn->passwd) { + conn->passwd = strdup(setpasswd); + if(!conn->passwd) + result = CURLE_OUT_OF_MEMORY; + } - if(!conn->options) + /* 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; } @@ -4009,12 +3579,7 @@ static CURLcode create_conn(struct Curl_easy *data, CURLcode result = CURLE_OK; struct connectdata *conn; struct connectdata *conn_temp = NULL; - size_t urllen; - char *user = NULL; - char *passwd = NULL; - char *options = NULL; bool reuse; - bool prot_missing = FALSE; bool connections_available = TRUE; bool force_reuse = FALSE; bool waitpipe = FALSE; @@ -4026,7 +3591,6 @@ static CURLcode create_conn(struct Curl_easy *data, /************************************************************* * Check input data *************************************************************/ - if(!data->change.url) { result = CURLE_URL_MALFORMAT; goto out; @@ -4048,107 +3612,10 @@ static CURLcode create_conn(struct Curl_easy *data, any failure */ *in_connect = conn; - /* This initing continues below, see the comment "Continue connectdata - * initialization here" */ - - /*********************************************************** - * We need to allocate memory to store the path in. We get the size of the - * full URL to be sure, and we need to make it at least 256 bytes since - * other parts of the code will rely on this fact - ***********************************************************/ -#define LEAST_PATH_ALLOC 256 - urllen = strlen(data->change.url); - if(urllen < LEAST_PATH_ALLOC) - urllen = LEAST_PATH_ALLOC; - - /* - * We malloc() the buffers below urllen+2 to make room for 2 possibilities: - * 1 - an extra terminating zero - * 2 - an extra slash (in case a syntax like "www.host.com?moo" is used) - */ - - Curl_safefree(data->state.pathbuffer); - data->state.path = NULL; - - data->state.pathbuffer = malloc(urllen + 2); - if(NULL == data->state.pathbuffer) { - result = CURLE_OUT_OF_MEMORY; /* really bad error */ - goto out; - } - data->state.path = data->state.pathbuffer; - - conn->host.rawalloc = malloc(urllen + 2); - if(NULL == conn->host.rawalloc) { - Curl_safefree(data->state.pathbuffer); - data->state.path = NULL; - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - conn->host.name = conn->host.rawalloc; - conn->host.name[0] = 0; - - user = strdup(""); - passwd = strdup(""); - options = strdup(""); - if(!user || !passwd || !options) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - result = parseurlandfillconn(data, conn, &prot_missing, &user, &passwd, - &options); + result = parseurlandfillconn(data, conn); if(result) goto out; - /************************************************************* - * No protocol part in URL was used, add it! - *************************************************************/ - if(prot_missing) { - /* We're guessing prefixes here and if we're told to use a proxy or if - we're going to follow a Location: later or... then we need the protocol - part added so that we have a valid URL. */ - char *reurl; - char *ch_lower; - - reurl = aprintf("%s://%s", conn->handler->scheme, data->change.url); - - if(!reurl) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - /* Change protocol prefix to lower-case */ - for(ch_lower = reurl; *ch_lower != ':'; ch_lower++) - *ch_lower = (char)TOLOWER(*ch_lower); - - if(data->change.url_alloc) { - Curl_safefree(data->change.url); - data->change.url_alloc = FALSE; - } - - data->change.url = reurl; - data->change.url_alloc = TRUE; /* free this later */ - } - - /************************************************************* - * If the protocol can't handle url query strings, then cut - * off the unhandable part - *************************************************************/ - if((conn->given->flags&PROTOPT_NOURLQUERY)) { - char *path_q_sep = strchr(conn->data->state.path, '?'); - if(path_q_sep) { - /* according to rfc3986, allow the query (?foo=bar) - also on protocols that can't handle it. - - cut the string-part after '?' - */ - - /* terminate the string */ - path_q_sep[0] = 0; - } - } - if(data->set.str[STRING_BEARER]) { conn->oauth_bearer = strdup(data->set.str[STRING_BEARER]); if(!conn->oauth_bearer) { @@ -4192,10 +3659,12 @@ static CURLcode create_conn(struct Curl_easy *data, /* Check for overridden login details and set them accordingly so they they are known when protocol->setup_connection is called! */ - result = override_login(data, conn, &user, &passwd, &options); + result = override_login(data, conn, &conn->user, &conn->passwd, + &conn->options); if(result) goto out; - result = set_login(conn, user, passwd, options); + + result = set_login(conn); /* default credentials */ if(result) goto out; @@ -4278,6 +3747,7 @@ static CURLcode create_conn(struct Curl_easy *data, /* this is supposed to be the connect function so we better at least check that the file is present here! */ DEBUGASSERT(conn->handler->connect_it); + Curl_persistconninfo(conn); result = conn->handler->connect_it(conn, &done); /* Setup a "faked" transfer that'll do nothing */ @@ -4381,6 +3851,9 @@ static CURLcode create_conn(struct Curl_easy *data, * new one. *************************************************************/ + DEBUGASSERT(conn->user); + DEBUGASSERT(conn->passwd); + /* reuse_fresh is TRUE if we are told to use a new connection by force, but we only acknowledge this option if this is not a re-used connection already (which happens due to follow-location or during a HTTP @@ -4556,10 +4029,6 @@ static CURLcode create_conn(struct Curl_easy *data, result = resolve_server(data, conn, async); out: - - free(options); - free(passwd); - free(user); return result; } @@ -4843,3 +4312,34 @@ 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 ef3ebf0..095d638 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h @@ -27,6 +27,18 @@ #define READBUFFER_MAX CURL_MAX_READ_SIZE #define READBUFFER_MIN 1024 +/* The default upload buffer size, should not be smaller than + CURL_MAX_WRITE_SIZE, as it needs to hold a full buffer as could be sent in + a write callback. + + The size was 16KB for many years but was bumped to 64KB because it makes + libcurl able to do significantly faster uploads in some circumstances. Even + larger buffers can help further, but this is deemed a fair memory/speed + compromise. */ +#define UPLOADBUFFER_DEFAULT 65536 +#define UPLOADBUFFER_MAX (2*1024*1024) +#define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE + /* * Prototypes for library-wide functions provided by url.c */ @@ -34,8 +46,11 @@ 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); -CURLcode Curl_dupset(struct Curl_easy * dst, struct Curl_easy * src); + void Curl_freeset(struct Curl_easy * data); +/* free the URL pieces */ +void Curl_up_free(struct Curl_easy *data); +CURLcode Curl_uc_to_curlcode(CURLUcode uc); CURLcode Curl_close(struct Curl_easy *data); /* opposite of curl_open() */ CURLcode Curl_connect(struct Curl_easy *, struct connectdata **, bool *async, bool *protocol_connect); @@ -57,9 +72,7 @@ int Curl_doing_getsock(struct connectdata *conn, CURLcode Curl_parse_login_details(const char *login, const size_t len, char **userptr, char **passwdptr, char **optionsptr); -bool Curl_isPipeliningEnabled(const struct Curl_easy *handle); -CURLcode Curl_addHandleToPipeline(struct Curl_easy *handle, - struct curl_llist *pipeline); + int Curl_removeHandleFromPipeline(struct Curl_easy *handle, struct curl_llist *pipeline); /* remove the specified connection from all (possible) pipelines and related @@ -67,7 +80,9 @@ int Curl_removeHandleFromPipeline(struct Curl_easy *handle, void Curl_getoff_all_pipelines(struct Curl_easy *data, struct connectdata *conn); -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); #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h new file mode 100644 index 0000000..a57d2e2 --- /dev/null +++ b/Utilities/cmcurl/lib/urlapi-int.h @@ -0,0 +1,33 @@ +#ifndef HEADER_CURL_URLAPI_INT_H +#define HEADER_CURL_URLAPI_INT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.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" +/* scheme is not URL encoded, the longest libcurl supported ones are 6 + letters */ +#define MAX_SCHEME_LEN 8 + +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); +#endif /* HEADER_CURL_URLAPI_INT_H */ diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c new file mode 100644 index 0000000..c53e523 --- /dev/null +++ b/Utilities/cmcurl/lib/urlapi.c @@ -0,0 +1,1340 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.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 "urldata.h" +#include "urlapi-int.h" +#include "strcase.h" +#include "dotdot.h" +#include "url.h" +#include "escape.h" +#include "curl_ctype.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + /* MSDOS/Windows style drive prefix, eg c: in c:foo */ +#define STARTS_WITH_DRIVE_PREFIX(str) \ + ((('a' <= str[0] && str[0] <= 'z') || \ + ('A' <= str[0] && str[0] <= 'Z')) && \ + (str[1] == ':')) + + /* MSDOS/Windows style drive prefix, optionally with + * a '|' instead of ':', followed by a slash or NUL */ +#define STARTS_WITH_URL_DRIVE_PREFIX(str) \ + ((('a' <= (str)[0] && (str)[0] <= 'z') || \ + ('A' <= (str)[0] && (str)[0] <= 'Z')) && \ + ((str)[1] == ':' || (str)[1] == '|') && \ + ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0)) + +/* Internal representation of CURLU. Point to URL-encoded strings. */ +struct Curl_URL { + char *scheme; + char *user; + char *password; + char *options; /* IMAP only? */ + char *host; + char *port; + char *path; + char *query; + char *fragment; + + char *scratch; /* temporary scratch area */ + long portnum; /* the numerical version */ +}; + +#define DEFAULT_SCHEME "https" + +static void free_urlhandle(struct Curl_URL *u) +{ + free(u->scheme); + free(u->user); + free(u->password); + free(u->options); + free(u->host); + free(u->port); + free(u->path); + free(u->query); + free(u->fragment); + free(u->scratch); +} + +/* move the full contents of one handle onto another and + free the original */ +static void mv_urlhandle(struct Curl_URL *from, + struct Curl_URL *to) +{ + free_urlhandle(to); + *to = *from; + free(from); +} + +/* + * Find the separator at the end of the host name, or the '?' in cases like + * http://www.url.com?id=2380 + */ +static const char *find_host_sep(const char *url) +{ + const char *sep; + const char *query; + + /* Find the start of the hostname */ + sep = strstr(url, "//"); + if(!sep) + sep = url; + else + sep += 2; + + query = strchr(sep, '?'); + sep = strchr(sep, '/'); + + if(!sep) + sep = url + strlen(url); + + if(!query) + query = url + strlen(url); + + return sep < query ? sep : query; +} + +/* + * Decide in an encoding-independent manner whether a character in an + * URL must be escaped. The same criterion must be used in strlen_url() + * and strcpy_url(). + */ +static bool urlchar_needs_escaping(int c) +{ + return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c)); +} + +/* + * strlen_url() returns the length of the given URL if the spaces within the + * URL were properly URL encoded. + * URL encoding should be skipped for host names, otherwise IDN resolution + * will fail. + */ +size_t Curl_strlen_url(const char *url, bool relative) +{ + const unsigned char *ptr; + size_t newlen = 0; + bool left = TRUE; /* left side of the ? */ + const unsigned char *host_sep = (const unsigned char *) url; + + if(!relative) + host_sep = (const unsigned char *) find_host_sep(url); + + for(ptr = (unsigned char *)url; *ptr; ptr++) { + + if(ptr < host_sep) { + ++newlen; + continue; + } + + switch(*ptr) { + case '?': + left = FALSE; + /* FALLTHROUGH */ + default: + if(urlchar_needs_escaping(*ptr)) + newlen += 2; + newlen++; + break; + case ' ': + if(left) + newlen += 3; + else + newlen++; + break; + } + } + return newlen; +} + +/* strcpy_url() copies a url to a output buffer and URL-encodes the spaces in + * the source URL accordingly. + * URL encoding should be skipped for host names, otherwise IDN resolution + * will fail. + */ +void Curl_strcpy_url(char *output, const char *url, bool relative) +{ + /* we must add this with whitespace-replacing */ + bool left = TRUE; + const unsigned char *iptr; + char *optr = output; + const unsigned char *host_sep = (const unsigned char *) url; + + if(!relative) + host_sep = (const unsigned char *) find_host_sep(url); + + for(iptr = (unsigned char *)url; /* read from here */ + *iptr; /* until zero byte */ + iptr++) { + + if(iptr < host_sep) { + *optr++ = *iptr; + continue; + } + + switch(*iptr) { + case '?': + left = FALSE; + /* FALLTHROUGH */ + default: + if(urlchar_needs_escaping(*iptr)) { + snprintf(optr, 4, "%%%02x", *iptr); + optr += 3; + } + else + *optr++=*iptr; + break; + case ' ': + if(left) { + *optr++='%'; /* add a '%' */ + *optr++='2'; /* add a '2' */ + *optr++='0'; /* add a '0' */ + } + else + *optr++='+'; /* add a '+' here */ + break; + } + } + *optr = 0; /* zero terminate output buffer */ + +} + +/* + * Returns true if the given URL is absolute (as opposed to relative) within + * the buffer size. Returns the scheme in the buffer if TRUE and 'buf' is + * non-NULL. + */ +bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen) +{ + size_t i; +#ifdef WIN32 + if(STARTS_WITH_DRIVE_PREFIX(url)) + return FALSE; +#endif + for(i = 0; i < buflen && url[i]; ++i) { + char s = url[i]; + if(s == ':') { + if(buf) + buf[i] = 0; + return TRUE; + } + /* RFC 3986 3.1 explains: + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + */ + else if(ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') ) { + if(buf) + buf[i] = (char)TOLOWER(s); + } + else + break; + } + return FALSE; +} + +/* + * Concatenate a relative URL to a base URL making it absolute. + * URL-encodes any spaces. + * The returned pointer must be freed by the caller unless NULL + * (returns NULL on out of memory). + */ +char *Curl_concat_url(const char *base, const char *relurl) +{ + /*** + TRY to append this new path to the old URL + to the right of the host part. Oh crap, this is doomed to cause + problems in the future... + */ + char *newest; + char *protsep; + char *pathsep; + size_t newlen; + bool host_changed = FALSE; + + const char *useurl = relurl; + size_t urllen; + + /* we must make our own copy of the URL to play with, as it may + point to read-only data */ + char *url_clone = strdup(base); + + if(!url_clone) + return NULL; /* skip out of this NOW */ + + /* protsep points to the start of the host name */ + protsep = strstr(url_clone, "//"); + if(!protsep) + protsep = url_clone; + else + protsep += 2; /* pass the slashes */ + + if('/' != relurl[0]) { + int level = 0; + + /* First we need to find out if there's a ?-letter in the URL, + and cut it and the right-side of that off */ + pathsep = strchr(protsep, '?'); + if(pathsep) + *pathsep = 0; + + /* we have a relative path to append to the last slash if there's one + available, or if the new URL is just a query string (starts with a + '?') we append the new one at the end of the entire currently worked + out URL */ + if(useurl[0] != '?') { + pathsep = strrchr(protsep, '/'); + if(pathsep) + *pathsep = 0; + } + + /* Check if there's any slash after the host name, and if so, remember + that position instead */ + pathsep = strchr(protsep, '/'); + if(pathsep) + protsep = pathsep + 1; + else + protsep = NULL; + + /* now deal with one "./" or any amount of "../" in the newurl + and act accordingly */ + + if((useurl[0] == '.') && (useurl[1] == '/')) + useurl += 2; /* just skip the "./" */ + + while((useurl[0] == '.') && + (useurl[1] == '.') && + (useurl[2] == '/')) { + level++; + useurl += 3; /* pass the "../" */ + } + + if(protsep) { + while(level--) { + /* cut off one more level from the right of the original URL */ + pathsep = strrchr(protsep, '/'); + if(pathsep) + *pathsep = 0; + else { + *protsep = 0; + break; + } + } + } + } + else { + /* We got a new absolute path for this server */ + + if((relurl[0] == '/') && (relurl[1] == '/')) { + /* the new URL starts with //, just keep the protocol part from the + original one */ + *protsep = 0; + useurl = &relurl[2]; /* we keep the slashes from the original, so we + skip the new ones */ + host_changed = TRUE; + } + else { + /* cut off the original URL from the first slash, or deal with URLs + without slash */ + pathsep = strchr(protsep, '/'); + if(pathsep) { + /* When people use badly formatted URLs, such as + "http://www.url.com?dir=/home/daniel" we must not use the first + slash, if there's a ?-letter before it! */ + char *sep = strchr(protsep, '?'); + if(sep && (sep < pathsep)) + pathsep = sep; + *pathsep = 0; + } + else { + /* There was no slash. Now, since we might be operating on a badly + formatted URL, such as "http://www.url.com?id=2380" which doesn't + use a slash separator as it is supposed to, we need to check for a + ?-letter as well! */ + pathsep = strchr(protsep, '?'); + if(pathsep) + *pathsep = 0; + } + } + } + + /* If the new part contains a space, this is a mighty stupid redirect + but we still make an effort to do "right". To the left of a '?' + letter we replace each space with %20 while it is replaced with '+' + on the right side of the '?' letter. + */ + newlen = Curl_strlen_url(useurl, !host_changed); + + urllen = strlen(url_clone); + + newest = malloc(urllen + 1 + /* possible slash */ + newlen + 1 /* zero byte */); + + if(!newest) { + free(url_clone); /* don't leak this */ + return NULL; + } + + /* copy over the root url part */ + memcpy(newest, url_clone, urllen); + + /* check if we need to append a slash */ + if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0])) + ; + else + newest[urllen++]='/'; + + /* then append the new piece on the right side */ + Curl_strcpy_url(&newest[urllen], useurl, !host_changed); + + free(url_clone); + + return newest; +} + +/* + * parse_hostname_login() + * + * Parse the login details (user name, password and options) from the URL and + * strip them out of the host name + * + */ +static CURLUcode parse_hostname_login(struct Curl_URL *u, + const struct Curl_handler *h, + char **hostname, + unsigned int flags) +{ + CURLUcode result = CURLUE_OK; + CURLcode ccode; + char *userp = NULL; + char *passwdp = NULL; + char *optionsp = NULL; + + /* At this point, we're hoping all the other special cases have + * been taken care of, so conn->host.name is at most + * [user[:password][;options]]@]hostname + * + * We need somewhere to put the embedded details, so do that first. + */ + + char *ptr = strchr(*hostname, '@'); + char *login = *hostname; + + if(!ptr) + goto out; + + /* We will now try to extract the + * possible login information in a string like: + * ftp://user:password@ftp.my.site:8021/README */ + *hostname = ++ptr; + + /* We could use the login information in the URL so extract it. Only parse + options if the handler says we should. Note that 'h' might be NULL! */ + ccode = Curl_parse_login_details(login, ptr - login - 1, + &userp, &passwdp, + (h && (h->flags & PROTOPT_URLOPTIONS)) ? + &optionsp:NULL); + if(ccode) { + result = CURLUE_MALFORMED_INPUT; + goto out; + } + + if(userp) { + if(flags & CURLU_DISALLOW_USER) { + /* Option DISALLOW_USER is set and url contains username. */ + result = CURLUE_USER_NOT_ALLOWED; + goto out; + } + + u->user = userp; + } + + if(passwdp) + u->password = passwdp; + + if(optionsp) + u->options = optionsp; + + return CURLUE_OK; + out: + + free(userp); + free(passwdp); + free(optionsp); + + return result; +} + +static CURLUcode parse_port(struct Curl_URL *u, char *hostname) +{ + char *portptr; + char endbracket; + int len; + + if((1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.%%]%c%n", + &endbracket, &len)) && + (']' == endbracket)) { + /* this is a RFC2732-style specified IP-address */ + portptr = &hostname[len]; + if (*portptr != ':') + return CURLUE_MALFORMED_INPUT; + } + else + portptr = strchr(hostname, ':'); + + if(portptr) { + char *rest; + long port; + char portbuf[7]; + + if(!ISDIGIT(portptr[1])) + return CURLUE_BAD_PORT_NUMBER; + + port = strtol(portptr + 1, &rest, 10); /* Port number must be decimal */ + + if((port <= 0) || (port > 0xffff)) + /* Single unix standard says port numbers are 16 bits long, but we don't + treat port zero as OK. */ + return CURLUE_BAD_PORT_NUMBER; + + if(rest[0]) + return CURLUE_BAD_PORT_NUMBER; + + if(rest != &portptr[1]) { + *portptr++ = '\0'; /* cut off the name there */ + *rest = 0; + /* generate a new to get rid of leading zeroes etc */ + snprintf(portbuf, sizeof(portbuf), "%ld", port); + u->portnum = port; + u->port = strdup(portbuf); + if(!u->port) + return CURLUE_OUT_OF_MEMORY; + } + else { + /* Browser behavior adaptation. If there's a colon with no digits after, + just cut off the name there which makes us ignore the colon and just + use the default port. Firefox and Chrome both do that. */ + *portptr = '\0'; + } + } + + return CURLUE_OK; +} + +/* scan for byte values < 31 or 127 */ +static CURLUcode junkscan(char *part) +{ + char badbytes[]={ + /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x7f, + 0x00 /* zero terminate */ + }; + if(part) { + size_t n = strlen(part); + size_t nfine = strcspn(part, badbytes); + if(nfine != n) + /* since we don't know which part is scanned, return a generic error + code */ + return CURLUE_MALFORMED_INPUT; + } + return CURLUE_OK; +} + +static CURLUcode hostname_check(char *hostname, unsigned int flags) +{ + const char *l = NULL; /* accepted characters */ + size_t len; + size_t hlen = strlen(hostname); + (void)flags; + + if(hostname[0] == '[') { + hostname++; + l = "0123456789abcdefABCDEF::.%"; + hlen -= 2; + } + + if(l) { + /* only valid letters are ok */ + len = strspn(hostname, l); + if(hlen != len) + /* hostname with bad content */ + return CURLUE_MALFORMED_INPUT; + } + else { + /* letters from the second string is not ok */ + len = strcspn(hostname, " "); + if(hlen != len) + /* hostname with bad content */ + return CURLUE_MALFORMED_INPUT; + } + return CURLUE_OK; +} + +#define HOSTNAME_END(x) (((x) == '/') || ((x) == '?') || ((x) == '#')) + +static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) +{ + char *path; + bool path_alloced = FALSE; + char *hostname; + char *query = NULL; + char *fragment = NULL; + CURLUcode result; + bool url_has_scheme = FALSE; + char schemebuf[MAX_SCHEME_LEN]; + char *schemep = NULL; + size_t schemelen = 0; + size_t urllen; + const struct Curl_handler *h = NULL; + + if(!url) + return CURLUE_MALFORMED_INPUT; + + /************************************************************* + * Parse the URL. + ************************************************************/ + /* allocate scratch area */ + urllen = strlen(url); + path = u->scratch = malloc(urllen * 2 + 2); + if(!path) + return CURLUE_OUT_OF_MEMORY; + + hostname = &path[urllen + 1]; + hostname[0] = 0; + + if(Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf))) { + url_has_scheme = TRUE; + schemelen = strlen(schemebuf); + } + + /* handle the file: scheme */ + if(url_has_scheme && strcasecompare(schemebuf, "file")) { + /* path has been allocated large enough to hold this */ + strcpy(path, &url[5]); + + hostname = NULL; /* no host for file: URLs */ + u->scheme = strdup("file"); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + + /* Extra handling URLs with an authority component (i.e. that start with + * "file://") + * + * We allow omitted hostname (e.g. file:/<path>) -- valid according to + * RFC 8089, but not the (current) WHAT-WG URL spec. + */ + if(path[0] == '/' && path[1] == '/') { + /* swallow the two slashes */ + char *ptr = &path[2]; + + /* + * According to RFC 8089, a file: URL can be reliably dereferenced if: + * + * o it has no/blank hostname, or + * + * o the hostname matches "localhost" (case-insensitively), or + * + * o the hostname is a FQDN that resolves to this machine. + * + * For brevity, we only consider URLs with empty, "localhost", or + * "127.0.0.1" hostnames as local. + * + * Additionally, there is an exception for URLs with a Windows drive + * letter in the authority (which was accidentally omitted from RFC 8089 + * Appendix E, but believe me, it was meant to be there. --MK) + */ + if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { + /* the URL includes a host name, it must match "localhost" or + "127.0.0.1" to be valid */ + if(!checkprefix("localhost/", ptr) && + !checkprefix("127.0.0.1/", ptr)) { + /* Invalid file://hostname/, expected localhost or 127.0.0.1 or + none */ + return CURLUE_MALFORMED_INPUT; + } + ptr += 9; /* now points to the slash after the host */ + } + + path = ptr; + } + +#if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) + /* Don't allow Windows drive letters when not in Windows. + * This catches both "file:/c:" and "file:c:" */ + if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || + STARTS_WITH_URL_DRIVE_PREFIX(path)) { + /* File drive letters are only accepted in MSDOS/Windows */ + return CURLUE_MALFORMED_INPUT; + } +#else + /* If the path starts with a slash and a drive letter, ditch the slash */ + if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { + /* This cannot be done with strcpy, as the memory chunks overlap! */ + memmove(path, &path[1], strlen(&path[1]) + 1); + } +#endif + + } + else { + /* clear path */ + const char *p; + const char *hostp; + size_t len; + path[0] = 0; + + if(url_has_scheme) { + int i = 0; + p = &url[schemelen + 1]; + while(p && (*p == '/') && (i < 4)) { + p++; + i++; + } + if((i < 1) || (i>3)) + /* less than one or more than three slashes */ + return CURLUE_MALFORMED_INPUT; + + schemep = schemebuf; + if(!Curl_builtin_scheme(schemep) && + !(flags & CURLU_NON_SUPPORT_SCHEME)) + return CURLUE_UNSUPPORTED_SCHEME; + + if(junkscan(schemep)) + return CURLUE_MALFORMED_INPUT; + } + else { + /* no scheme! */ + + if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME))) + return CURLUE_MALFORMED_INPUT; + if(flags & CURLU_DEFAULT_SCHEME) + schemep = (char *) DEFAULT_SCHEME; + + /* + * The URL was badly formatted, let's try without scheme specified. + */ + p = url; + } + hostp = p; /* host name starts here */ + + while(*p && !HOSTNAME_END(*p)) /* find end of host name */ + 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"; + } + + len = strlen(p); + memcpy(path, p, len); + path[len] = 0; + + 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; + + fragment = strchr(query?query:path, '#'); + if(fragment) + *fragment++ = 0; + + if(!path[0]) + /* if there's no path 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; + + if(strcmp(newp, path)) { + /* if we got a new version */ + path = newp; + path_alloced = TRUE; + } + else + free(newp); + } + if(path) { + u->path = path_alloced?path:strdup(path); + if(!u->path) + return CURLUE_OUT_OF_MEMORY; + } + + if(hostname) { + /* + * Parse the login details and strip them out of the host name. + */ + if(junkscan(hostname)) + return CURLUE_MALFORMED_INPUT; + + result = parse_hostname_login(u, h, &hostname, flags); + if(result) + return result; + + result = parse_port(u, hostname); + if(result) + return result; + + result = hostname_check(hostname, flags); + if(result) + return result; + + u->host = strdup(hostname); + if(!u->host) + return CURLUE_OUT_OF_MEMORY; + } + + if(query && query[0]) { + 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; + } + + free(u->scratch); + u->scratch = NULL; + + return CURLUE_OK; +} + +/* + * Parse the URL and set the relevant members of the Curl_URL struct. + */ +static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) +{ + CURLUcode result = seturl(url, u, flags); + if(result) { + free_urlhandle(u); + memset(u, 0, sizeof(struct Curl_URL)); + } + return result; +} + +/* + */ +CURLU *curl_url(void) +{ + return calloc(sizeof(struct Curl_URL), 1); +} + +void curl_url_cleanup(CURLU *u) +{ + if(u) { + free_urlhandle(u); + free(u); + } +} + +#define DUP(dest, src, name) \ + if(src->name) { \ + dest->name = strdup(src->name); \ + if(!dest->name) \ + goto fail; \ + } + +CURLU *curl_url_dup(CURLU *in) +{ + struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1); + if(u) { + DUP(u, in, scheme); + DUP(u, in, user); + DUP(u, in, password); + DUP(u, in, options); + DUP(u, in, host); + DUP(u, in, port); + DUP(u, in, path); + DUP(u, in, query); + DUP(u, in, fragment); + u->portnum = in->portnum; + } + return u; + fail: + curl_url_cleanup(u); + return NULL; +} + +CURLUcode curl_url_get(CURLU *u, CURLUPart what, + char **part, unsigned int flags) +{ + char *ptr; + CURLUcode ifmissing = CURLUE_UNKNOWN_PART; + char portbuf[7]; + bool urldecode = (flags & CURLU_URLDECODE)?1:0; + bool plusdecode = FALSE; + (void)flags; + if(!u) + return CURLUE_BAD_HANDLE; + if(!part) + return CURLUE_BAD_PARTPOINTER; + *part = NULL; + + switch(what) { + case CURLUPART_SCHEME: + ptr = u->scheme; + ifmissing = CURLUE_NO_SCHEME; + urldecode = FALSE; /* never for schemes */ + break; + case CURLUPART_USER: + ptr = u->user; + ifmissing = CURLUE_NO_USER; + break; + case CURLUPART_PASSWORD: + ptr = u->password; + ifmissing = CURLUE_NO_PASSWORD; + break; + case CURLUPART_OPTIONS: + ptr = u->options; + ifmissing = CURLUE_NO_OPTIONS; + break; + case CURLUPART_HOST: + ptr = u->host; + ifmissing = CURLUE_NO_HOST; + break; + case CURLUPART_PORT: + ptr = u->port; + ifmissing = CURLUE_NO_PORT; + urldecode = FALSE; /* never for port */ + if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { + /* there's no stored port number, but asked to deliver + a default one for the scheme */ + const struct Curl_handler *h = + Curl_builtin_scheme(u->scheme); + if(h) { + snprintf(portbuf, sizeof(portbuf), "%ld", h->defport); + ptr = portbuf; + } + } + else if(ptr && u->scheme) { + /* there is a stored port number, but ask to inhibit if + it matches the default one for the scheme */ + const struct Curl_handler *h = + Curl_builtin_scheme(u->scheme); + if(h && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) + ptr = NULL; + } + break; + case CURLUPART_PATH: + ptr = u->path; + if(!ptr) { + ptr = u->path = strdup("/"); + if(!u->path) + return CURLUE_OUT_OF_MEMORY; + } + break; + case CURLUPART_QUERY: + ptr = u->query; + ifmissing = CURLUE_NO_QUERY; + plusdecode = urldecode; + break; + case CURLUPART_FRAGMENT: + ptr = u->fragment; + ifmissing = CURLUE_NO_FRAGMENT; + break; + case CURLUPART_URL: { + char *url; + char *scheme; + char *options = u->options; + char *port = u->port; + if(u->scheme && strcasecompare("file", u->scheme)) { + url = aprintf("file://%s%s%s", + u->path, + u->fragment? "#": "", + u->fragment? u->fragment : ""); + } + else if(!u->host) + return CURLUE_NO_HOST; + else { + const struct Curl_handler *h = NULL; + if(u->scheme) + scheme = u->scheme; + else if(flags & CURLU_DEFAULT_SCHEME) + scheme = (char *) DEFAULT_SCHEME; + 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) { + snprintf(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; + + url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + scheme, + u->user ? u->user : "", + u->password ? ":": "", + u->password ? u->password : "", + options ? ";" : "", + options ? options : "", + (u->user || u->password || options) ? "@": "", + u->host, + port ? ":": "", + port ? port : "", + (u->path && (u->path[0] != '/')) ? "/": "", + u->path ? u->path : "/", + u->query? "?": "", + u->query? u->query : "", + u->fragment? "#": "", + u->fragment? u->fragment : ""); + } + if(!url) + return CURLUE_OUT_OF_MEMORY; + *part = url; + return CURLUE_OK; + break; + } + default: + ptr = NULL; + } + if(ptr) { + *part = strdup(ptr); + if(!*part) + return CURLUE_OUT_OF_MEMORY; + if(plusdecode) { + /* convert + to space */ + char *plus; + for(plus = *part; *plus; ++plus) { + if(*plus == '+') + *plus = ' '; + } + } + if(urldecode) { + char *decoded; + size_t dlen; + CURLcode res = Curl_urldecode(NULL, *part, 0, &decoded, &dlen, TRUE); + free(*part); + if(res) { + *part = NULL; + return CURLUE_URLDECODE; + } + *part = decoded; + } + return CURLUE_OK; + } + else + return ifmissing; +} + +CURLUcode curl_url_set(CURLU *u, CURLUPart what, + const char *part, unsigned int flags) +{ + char **storep = NULL; + long port = 0; + bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; + bool plusencode = FALSE; + bool urlskipslash = FALSE; + bool appendquery = FALSE; + + if(!u) + return CURLUE_BAD_HANDLE; + if(!part) { + /* setting a part to NULL clears it */ + switch(what) { + case CURLUPART_URL: + break; + case CURLUPART_SCHEME: + storep = &u->scheme; + break; + case CURLUPART_USER: + storep = &u->user; + break; + case CURLUPART_PASSWORD: + storep = &u->password; + break; + case CURLUPART_OPTIONS: + storep = &u->options; + break; + case CURLUPART_HOST: + storep = &u->host; + break; + case CURLUPART_PORT: + storep = &u->port; + break; + case CURLUPART_PATH: + storep = &u->path; + break; + case CURLUPART_QUERY: + storep = &u->query; + break; + case CURLUPART_FRAGMENT: + storep = &u->fragment; + break; + default: + return CURLUE_UNKNOWN_PART; + } + if(storep && *storep) { + free(*storep); + *storep = NULL; + } + return CURLUE_OK; + } + + switch(what) { + case CURLUPART_SCHEME: + if(!(flags & CURLU_NON_SUPPORT_SCHEME) && + /* verify that it is a fine scheme */ + !Curl_builtin_scheme(part)) + return CURLUE_UNSUPPORTED_SCHEME; + storep = &u->scheme; + urlencode = FALSE; /* never */ + break; + case CURLUPART_USER: + storep = &u->user; + break; + case CURLUPART_PASSWORD: + storep = &u->password; + break; + case CURLUPART_OPTIONS: + storep = &u->options; + break; + case CURLUPART_HOST: + storep = &u->host; + break; + case CURLUPART_PORT: + urlencode = FALSE; /* never */ + port = strtol(part, NULL, 10); /* Port number must be decimal */ + if((port <= 0) || (port > 0xffff)) + return CURLUE_BAD_PORT_NUMBER; + storep = &u->port; + break; + case CURLUPART_PATH: + urlskipslash = TRUE; + storep = &u->path; + break; + case CURLUPART_QUERY: + plusencode = urlencode; + appendquery = (flags & CURLU_APPENDQUERY)?1:0; + storep = &u->query; + break; + case CURLUPART_FRAGMENT: + storep = &u->fragment; + break; + case CURLUPART_URL: { + /* + * Allow a new URL to replace the existing (if any) contents. + * + * If the existing contents is enough for a URL, allow a relative URL to + * replace it. + */ + CURLUcode result; + char *oldurl; + char *redired_url; + CURLU *handle2; + + if(Curl_is_absolute_url(part, NULL, MAX_SCHEME_LEN)) { + handle2 = curl_url(); + if(!handle2) + return CURLUE_OUT_OF_MEMORY; + result = parseurl(part, handle2, flags); + if(!result) + mv_urlhandle(handle2, u); + else + curl_url_cleanup(handle2); + return result; + } + /* extract the full "old" URL to do the redirect on */ + result = curl_url_get(u, CURLUPART_URL, &oldurl, flags); + if(result) { + /* couldn't get the old URL, just use the new! */ + handle2 = curl_url(); + if(!handle2) + return CURLUE_OUT_OF_MEMORY; + result = parseurl(part, handle2, flags); + if(!result) + mv_urlhandle(handle2, u); + else + curl_url_cleanup(handle2); + return result; + } + + /* apply the relative part to create a new URL */ + redired_url = Curl_concat_url(oldurl, part); + free(oldurl); + if(!redired_url) + return CURLUE_OUT_OF_MEMORY; + + /* now parse the new URL */ + handle2 = curl_url(); + if(!handle2) { + free(redired_url); + return CURLUE_OUT_OF_MEMORY; + } + result = parseurl(redired_url, handle2, flags); + free(redired_url); + if(!result) + mv_urlhandle(handle2, u); + else + curl_url_cleanup(handle2); + return result; + } + default: + return CURLUE_UNKNOWN_PART; + } + if(storep) { + const char *newp = part; + size_t nalloc = strlen(part); + + if(urlencode) { + const char *i; + char *o; + bool free_part = FALSE; + char *enc = malloc(nalloc * 3 + 1); /* for worst case! */ + if(!enc) + return CURLUE_OUT_OF_MEMORY; + if(plusencode) { + /* space to plus */ + i = part; + for(o = enc; *i; ++o, ++i) + *o = (*i == ' ') ? '+' : *i; + *o = 0; /* zero terminate */ + part = strdup(enc); + if(!part) { + free(enc); + return CURLUE_OUT_OF_MEMORY; + } + free_part = TRUE; + } + for(i = part, o = enc; *i; i++) { + if(Curl_isunreserved(*i) || + ((*i == '/') && urlskipslash) || + ((*i == '=') && appendquery) || + ((*i == '+') && plusencode)) { + *o = *i; + o++; + } + else { + snprintf(o, 4, "%%%02x", *i); + o += 3; + } + } + *o = 0; /* zero terminate */ + newp = enc; + if(free_part) + free((char *)part); + } + else { + char *p; + newp = strdup(part); + if(!newp) + return CURLUE_OUT_OF_MEMORY; + p = (char *)newp; + while(*p) { + /* make sure percent encoded are lower case */ + if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && + (ISUPPER(p[1]) || ISUPPER(p[2]))) { + p[1] = (char)TOLOWER(p[1]); + p[2] = (char)TOLOWER(p[2]); + p += 3; + } + else + p++; + } + } + + if(appendquery) { + /* Append the string onto the old query. Add a '&' separator if none is + present at the end of the exsting query already */ + size_t querylen = u->query ? strlen(u->query) : 0; + bool addamperand = querylen && (u->query[querylen -1] != '&'); + if(querylen) { + size_t newplen = strlen(newp); + char *p = malloc(querylen + addamperand + newplen + 1); + if(!p) { + free((char *)newp); + return CURLUE_OUT_OF_MEMORY; + } + strcpy(p, u->query); /* original query */ + if(addamperand) + p[querylen] = '&'; /* ampersand */ + strcpy(&p[querylen + addamperand], newp); /* new suffix */ + free((char *)newp); + free(*storep); + *storep = p; + return CURLUE_OK; + } + } + + free(*storep); + *storep = (char *)newp; + } + /* set after the string, to make it not assigned if the allocation above + fails */ + if(port) + u->portnum = port; + return CURLUE_OK; +} diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index 67db3b2..11a6a22 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -142,14 +142,6 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */ #include <libssh2_sftp.h> #endif /* HAVE_LIBSSH2_H */ -/* The upload buffer size, should not be smaller than CURL_MAX_WRITE_SIZE, as - it needs to hold a full buffer as could be sent in a write callback. - - The size was 16KB for many years but was bumped to 64KB because it makes - libcurl able to do significantly faster uploads in some circumstances. Even - larger buffers can help further, but this is deemed a fair memory/speed - compromise. */ -#define UPLOAD_BUFSIZE 65536 /* The "master buffer" is for HTTP pipelining */ #define MASTERBUF_SIZE 16384 @@ -476,7 +468,6 @@ struct hostname { #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE) -#ifdef CURLRES_ASYNCH struct Curl_async { char *hostname; int port; @@ -485,7 +476,6 @@ struct Curl_async { int status; /* if done is TRUE, this is the status from the callback */ void *os_specific; /* 'struct thread_data' for Windows */ }; -#endif #define FIRSTSOCKET 0 #define SECONDARYSOCKET 1 @@ -511,6 +501,28 @@ enum upgrade101 { UPGR101_WORKING /* talking upgraded protocol */ }; +struct dohresponse { + unsigned char *memory; + size_t size; +}; + +/* one of these for each DoH request */ +struct dnsprobe { + CURL *easy; + int dnstype; + unsigned char dohbuffer[512]; + size_t dohlen; + struct dohresponse serverdoh; +}; + +struct dohdata { + struct curl_slist *headers; + struct dnsprobe probe[2]; + unsigned int pending; /* still outstanding requests */ + const char *host; + int port; +}; + /* * Request specific data in the easy handle (Curl_easy). Previously, * these members were on the connectdata struct but since a conn struct may @@ -606,6 +618,7 @@ struct SingleRequest { void *protop; /* Allocated protocol-specific data. Each protocol handler makes sure this points to data it needs. */ + struct dohdata doh; /* DoH specific data for this request */ }; /* @@ -636,7 +649,7 @@ struct Curl_handler { */ CURLcode (*connect_it)(struct connectdata *, bool *done); - /* See above. Currently only used for FTP. */ + /* See above. */ CURLcode (*connecting)(struct connectdata *, bool *done); CURLcode (*doing)(struct connectdata *, bool *done); @@ -716,6 +729,7 @@ struct Curl_handler { #define CONNCHECK_NONE 0 /* No checks */ #define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */ +#define CONNCHECK_KEEPALIVE (1<<1) /* Perform any keepalive function. */ #define CONNRESULT_NONE 0 /* No extra information. */ #define CONNRESULT_DEAD (1<<0) /* The connection is dead. */ @@ -892,6 +906,13 @@ struct connectdata { long ip_version; /* copied from the Curl_easy at creation time */ + /* Protocols can use a custom keepalive mechanism to keep connections alive. + This allows those protocols to track the last time the keepalive mechanism + was used on this connection. */ + struct curltime keepalive; + + long upkeep_interval_ms; /* Time between calls for connection upkeep. */ + /**** curl_get() phase fields */ curl_socket_t sockfd; /* socket to read from or CURL_SOCKET_BAD */ @@ -969,11 +990,8 @@ struct connectdata { #endif char syserr_buf [256]; /* buffer for Curl_strerror() */ - -#ifdef CURLRES_ASYNCH /* data used for the asynch name resolve callback */ struct Curl_async async; -#endif /* These three are used for chunked-encoding trailer support */ char *trailer; /* allocated buffer to store trailer in */ @@ -1206,6 +1224,18 @@ struct time_node { expire_id eid; }; +/* individual pieces of the URL */ +struct urlpieces { + char *scheme; + char *hostname; + char *port; + char *user; + char *password; + char *options; + char *path; + char *query; +}; + struct UrlState { /* Points to the connection cache */ @@ -1225,7 +1255,7 @@ struct UrlState { size_t headersize; /* size of the allocation */ char *buffer; /* download buffer */ - char *ulbuf; /* alloced upload buffer or NULL */ + char *ulbuf; /* allocated upload buffer or NULL */ curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ bool this_is_a_follow; /* this is a followed Location: request */ @@ -1296,9 +1326,6 @@ struct UrlState { /* for FTP downloads: how many CRLFs did we converted to LFs? */ curl_off_t crlf_conversions; #endif - char *pathbuffer;/* allocated buffer to store the URL's path part in */ - char *path; /* path to use, points to somewhere within the pathbuffer - area */ bool slash_removed; /* set TRUE if the 'path' points to a path where the initial URL slash separator has been taken off */ bool use_range; @@ -1332,6 +1359,8 @@ struct UrlState { #ifdef CURLDEBUG bool conncache_lock; #endif + CURLU *uh; /* URL handle for the current parsed URL */ + struct urlpieces up; }; @@ -1442,18 +1471,23 @@ enum dupstring { STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */ #endif STRING_TARGET, /* CURLOPT_REQUEST_TARGET */ + STRING_DOH, /* CURLOPT_DOH_URL */ /* -- end of zero-terminated strings -- */ STRING_LASTZEROTERMINATED, - /* -- below this are pointers to binary data that cannot be strdup'ed. - Each such pointer must be added manually to Curl_dupset() --- */ + /* -- below this are pointers to binary data that cannot be strdup'ed. --- */ STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ STRING_LAST /* not used, just an end-of-list marker */ }; +/* callback that gets called when this easy handle is completed within a multi + handle. Only used for internally created transfers, like for example + DoH. */ +typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result); + struct UserDefined { FILE *err; /* the stderr user data goes here */ void *debugdata; /* the data that will be passed to fdebug */ @@ -1562,8 +1596,8 @@ struct UserDefined { curl_proxytype proxytype; /* what kind of proxy that is in use */ long dns_cache_timeout; /* DNS cache timeout */ long buffer_size; /* size of receive buffer to use */ - long upload_buffer_size; /* size of upload buffer to use, - keep it >= CURL_MAX_WRITE_SIZE */ + size_t upload_buffer_size; /* size of upload buffer to use, + keep it >= CURL_MAX_WRITE_SIZE */ void *private_data; /* application-private data */ struct curl_slist *http200aliases; /* linked list of aliases for http200 */ @@ -1689,6 +1723,11 @@ struct UserDefined { before resolver start */ void *resolver_start_client; /* pointer to pass to resolver start callback */ bool disallow_username_in_url; /* disallow username in url */ + long upkeep_interval_ms; /* Time between calls for connection upkeep. */ + bool doh; /* DNS-over-HTTPS enabled */ + bool doh_get; /* use GET for DoH requests, instead of POST */ + multidone_func fmultidone; + struct Curl_easy *dohfor; /* this is a DoH request for that transfer */ }; struct Names { diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c index 5d61ce6..be6d611 100644 --- a/Utilities/cmcurl/lib/vauth/cleartext.c +++ b/Utilities/cmcurl/lib/vauth/cleartext.c @@ -50,7 +50,7 @@ * * data [in] - The session handle. * userp [in] - The user name. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. @@ -74,7 +74,7 @@ CURLcode Curl_auth_create_plain_message(struct Curl_easy *data, plen = strlen(passwdp); /* Compute binary message length. Check for overflows. */ - if((ulen > SIZE_T_MAX/2) || (plen > (SIZE_T_MAX/2 - 2))) + if((ulen > SIZE_T_MAX/4) || (plen > (SIZE_T_MAX/2 - 2))) return CURLE_OUT_OF_MEMORY; plainlen = 2 * ulen + plen + 2; diff --git a/Utilities/cmcurl/lib/vauth/cram.c b/Utilities/cmcurl/lib/vauth/cram.c index 3074a16..d148618 100644 --- a/Utilities/cmcurl/lib/vauth/cram.c +++ b/Utilities/cmcurl/lib/vauth/cram.c @@ -81,7 +81,7 @@ CURLcode Curl_auth_decode_cram_md5_message(const char *chlg64, char **outptr, * data [in] - The session handle. * chlg [in] - The challenge. * userp [in] - The user name. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index cc6f169..ab5156e 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -342,7 +342,7 @@ bool Curl_auth_is_digest_supported(void) * data [in] - The session handle. * chlg64 [in] - The base64 encoded challenge message. * userp [in] - The user name. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. @@ -668,7 +668,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * * data [in] - The session handle. * userp [in] - The user name. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. @@ -781,6 +781,8 @@ static CURLcode _Curl_auth_create_digest_http_message( */ hashthis = (unsigned char *) 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 at the moment. @@ -932,7 +934,7 @@ static CURLcode _Curl_auth_create_digest_http_message( * * data [in] - The session handle. * userp [in] - The user name. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c index a3f96ed..9287557 100644 --- a/Utilities/cmcurl/lib/vauth/digest_sspi.c +++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c @@ -75,7 +75,7 @@ bool Curl_auth_is_digest_supported(void) * data [in] - The session handle. * chlg64 [in] - The base64 encoded challenge message. * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. @@ -391,7 +391,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c index 560ecc5..55daec1 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c @@ -65,7 +65,7 @@ bool Curl_auth_is_gssapi_supported(void) * * data [in] - The session handle. * userp [in] - The user name. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * host [in[ - The host name. * mutual_auth [in] - Flag specifying whether or not mutual authentication diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c index 9afb971..cb11ed9 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c @@ -71,7 +71,7 @@ bool Curl_auth_is_gssapi_supported(void) * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * host [in] - The host name. * mutual_auth [in] - Flag specifying whether or not mutual authentication diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c index cdb8d8f..11f42f5 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.c +++ b/Utilities/cmcurl/lib/vauth/ntlm.c @@ -354,7 +354,7 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * host [in] - The host name. * ntlm [in/out] - The NTLM data struct being used and modified. @@ -481,7 +481,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * ntlm [in/out] - The NTLM data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c index 089c1a6..b66cfe7 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c +++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c @@ -69,7 +69,7 @@ bool Curl_auth_is_ntlm_supported(void) * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * host [in] - The host name. * ntlm [in/out] - The NTLM data struct being used and modified. @@ -234,7 +234,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * ntlm [in/out] - The NTLM data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c index 5196c27..4a48bdd 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c @@ -64,7 +64,7 @@ bool Curl_auth_is_spnego_supported(void) * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * host [in] - The host name. * chlg64 [in] - The optional base64 encoded challenge message. diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c index 1fe19e3..77d1895 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c @@ -71,8 +71,8 @@ bool Curl_auth_is_spnego_supported(void) * Parameters: * * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. + * user [in] - The user name in the format User or Domain\User. + * password [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * host [in] - The host name. * chlg64 [in] - The optional base64 encoded challenge message. diff --git a/Utilities/cmcurl/lib/vtls/axtls.h b/Utilities/cmcurl/lib/vtls/axtls.h index 3f1e129..cb81872 100644 --- a/Utilities/cmcurl/lib/vtls/axtls.h +++ b/Utilities/cmcurl/lib/vtls/axtls.h @@ -31,4 +31,3 @@ extern const struct Curl_ssl Curl_ssl_axtls; #endif /* USE_AXTLS */ #endif /* HEADER_CURL_AXTLS_H */ - diff --git a/Utilities/cmcurl/lib/vtls/darwinssl.c b/Utilities/cmcurl/lib/vtls/darwinssl.c index 1aea0dc..e8116b8 100644 --- a/Utilities/cmcurl/lib/vtls/darwinssl.c +++ b/Utilities/cmcurl/lib/vtls/darwinssl.c @@ -64,6 +64,7 @@ #define CURL_BUILD_IOS 0 #define CURL_BUILD_IOS_7 0 +#define CURL_BUILD_IOS_9 0 #define CURL_BUILD_IOS_11 0 #define CURL_BUILD_MAC 1 /* This is the maximum API level we are allowed to use when building: */ @@ -72,6 +73,7 @@ #define CURL_BUILD_MAC_10_7 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 #define CURL_BUILD_MAC_10_8 MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 #define CURL_BUILD_MAC_10_9 MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 +#define CURL_BUILD_MAC_10_11 MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 #define CURL_BUILD_MAC_10_13 MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 /* These macros mean "the following code is present to allow runtime backward compatibility with at least this cat or earlier": @@ -86,6 +88,7 @@ #elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE #define CURL_BUILD_IOS 1 #define CURL_BUILD_IOS_7 __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +#define CURL_BUILD_IOS_9 __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 #define CURL_BUILD_IOS_11 __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 #define CURL_BUILD_MAC 0 #define CURL_BUILD_MAC_10_5 0 @@ -93,6 +96,7 @@ #define CURL_BUILD_MAC_10_7 0 #define CURL_BUILD_MAC_10_8 0 #define CURL_BUILD_MAC_10_9 0 +#define CURL_BUILD_MAC_10_11 0 #define CURL_BUILD_MAC_10_13 0 #define CURL_SUPPORT_MAC_10_5 0 #define CURL_SUPPORT_MAC_10_6 0 @@ -116,6 +120,7 @@ #include "vtls.h" #include "darwinssl.h" #include "curl_printf.h" +#include "strdup.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -945,7 +950,7 @@ static CURLcode CopyCertSubject(struct Curl_easy *data, if(!c) { failf(data, "SSL: invalid CA certificate subject"); - return CURLE_OUT_OF_MEMORY; + return CURLE_SSL_CACERT; } /* If the subject is already available as UTF-8 encoded (ie 'direct') then @@ -1299,8 +1304,6 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) switch(ssl_version_max) { case CURL_SSLVERSION_MAX_NONE: - ssl_version_max = ssl_version << 16; - break; case CURL_SSLVERSION_MAX_DEFAULT: ssl_version_max = max_supported_version_by_os; break; @@ -1646,6 +1649,8 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, } CFRelease(cert); + if(result == CURLE_SSL_CACERT) + return CURLE_SSL_CERTPROBLEM; if(result) return result; } @@ -1781,107 +1786,118 @@ static CURLcode darwinssl_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.) */ - (void)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); + return CURLE_SSL_CIPHER; + } all_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); - allowed_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); - if(all_ciphers && allowed_ciphers && - SSLGetSupportedCiphers(BACKEND->ssl_ctx, all_ciphers, - &all_ciphers_count) == noErr) { - for(i = 0UL ; i < all_ciphers_count ; i++) { -#if CURL_BUILD_MAC - /* There's a known bug in early versions of Mountain Lion where ST's ECC - ciphers (cipher suite 0xC001 through 0xC032) simply do not work. - Work around the problem here by disabling those ciphers if we are - running in an affected version of OS X. */ - if(darwinver_maj == 12 && darwinver_min <= 3 && - all_ciphers[i] >= 0xC001 && all_ciphers[i] <= 0xC032) { - continue; - } -#endif /* CURL_BUILD_MAC */ - switch(all_ciphers[i]) { - /* Disable NULL ciphersuites: */ - case SSL_NULL_WITH_NULL_NULL: - case SSL_RSA_WITH_NULL_MD5: - case SSL_RSA_WITH_NULL_SHA: - case 0x003B: /* TLS_RSA_WITH_NULL_SHA256 */ - case SSL_FORTEZZA_DMS_WITH_NULL_SHA: - case 0xC001: /* TLS_ECDH_ECDSA_WITH_NULL_SHA */ - case 0xC006: /* TLS_ECDHE_ECDSA_WITH_NULL_SHA */ - case 0xC00B: /* TLS_ECDH_RSA_WITH_NULL_SHA */ - case 0xC010: /* TLS_ECDHE_RSA_WITH_NULL_SHA */ - case 0x002C: /* TLS_PSK_WITH_NULL_SHA */ - case 0x002D: /* TLS_DHE_PSK_WITH_NULL_SHA */ - case 0x002E: /* TLS_RSA_PSK_WITH_NULL_SHA */ - case 0x00B0: /* TLS_PSK_WITH_NULL_SHA256 */ - case 0x00B1: /* TLS_PSK_WITH_NULL_SHA384 */ - case 0x00B4: /* TLS_DHE_PSK_WITH_NULL_SHA256 */ - case 0x00B5: /* TLS_DHE_PSK_WITH_NULL_SHA384 */ - case 0x00B8: /* TLS_RSA_PSK_WITH_NULL_SHA256 */ - case 0x00B9: /* TLS_RSA_PSK_WITH_NULL_SHA384 */ - /* Disable anonymous ciphersuites: */ - case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: - case SSL_DH_anon_WITH_RC4_128_MD5: - case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: - case SSL_DH_anon_WITH_DES_CBC_SHA: - case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: - case TLS_DH_anon_WITH_AES_128_CBC_SHA: - case TLS_DH_anon_WITH_AES_256_CBC_SHA: - case 0xC015: /* TLS_ECDH_anon_WITH_NULL_SHA */ - case 0xC016: /* TLS_ECDH_anon_WITH_RC4_128_SHA */ - case 0xC017: /* TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA */ - case 0xC018: /* TLS_ECDH_anon_WITH_AES_128_CBC_SHA */ - case 0xC019: /* TLS_ECDH_anon_WITH_AES_256_CBC_SHA */ - case 0x006C: /* TLS_DH_anon_WITH_AES_128_CBC_SHA256 */ - case 0x006D: /* TLS_DH_anon_WITH_AES_256_CBC_SHA256 */ - case 0x00A6: /* TLS_DH_anon_WITH_AES_128_GCM_SHA256 */ - case 0x00A7: /* TLS_DH_anon_WITH_AES_256_GCM_SHA384 */ - /* Disable weak key ciphersuites: */ - case SSL_RSA_EXPORT_WITH_RC4_40_MD5: - case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: - case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: - case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: - case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: - case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: - case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: - case SSL_RSA_WITH_DES_CBC_SHA: - case SSL_DH_DSS_WITH_DES_CBC_SHA: - case SSL_DH_RSA_WITH_DES_CBC_SHA: - case SSL_DHE_DSS_WITH_DES_CBC_SHA: - case SSL_DHE_RSA_WITH_DES_CBC_SHA: - /* Disable IDEA: */ - case SSL_RSA_WITH_IDEA_CBC_SHA: - case SSL_RSA_WITH_IDEA_CBC_MD5: - /* Disable RC4: */ - case SSL_RSA_WITH_RC4_128_MD5: - case SSL_RSA_WITH_RC4_128_SHA: - case 0xC002: /* TLS_ECDH_ECDSA_WITH_RC4_128_SHA */ - case 0xC007: /* TLS_ECDHE_ECDSA_WITH_RC4_128_SHA*/ - case 0xC00C: /* TLS_ECDH_RSA_WITH_RC4_128_SHA */ - case 0xC011: /* TLS_ECDHE_RSA_WITH_RC4_128_SHA */ - case 0x008A: /* TLS_PSK_WITH_RC4_128_SHA */ - case 0x008E: /* TLS_DHE_PSK_WITH_RC4_128_SHA */ - case 0x0092: /* TLS_RSA_PSK_WITH_RC4_128_SHA */ - break; - default: /* enable everything else */ - allowed_ciphers[allowed_ciphers_count++] = all_ciphers[i]; - break; - } - } - err = SSLSetEnabledCiphers(BACKEND->ssl_ctx, allowed_ciphers, - allowed_ciphers_count); - if(err != noErr) { - failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } + if(!all_ciphers) { + failf(data, "SSL: Failed to allocate memory for all ciphers"); + return CURLE_OUT_OF_MEMORY; } - else { + allowed_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); + if(!allowed_ciphers) { Curl_safefree(all_ciphers); - Curl_safefree(allowed_ciphers); failf(data, "SSL: Failed to allocate memory for allowed ciphers"); return CURLE_OUT_OF_MEMORY; } + err = SSLGetSupportedCiphers(BACKEND->ssl_ctx, all_ciphers, + &all_ciphers_count); + if(err != noErr) { + Curl_safefree(all_ciphers); + Curl_safefree(allowed_ciphers); + return CURLE_SSL_CIPHER; + } + for(i = 0UL ; i < all_ciphers_count ; i++) { +#if CURL_BUILD_MAC + /* There's a known bug in early versions of Mountain Lion where ST's ECC + ciphers (cipher suite 0xC001 through 0xC032) simply do not work. + Work around the problem here by disabling those ciphers if we are + running in an affected version of OS X. */ + if(darwinver_maj == 12 && darwinver_min <= 3 && + all_ciphers[i] >= 0xC001 && all_ciphers[i] <= 0xC032) { + continue; + } +#endif /* CURL_BUILD_MAC */ + switch(all_ciphers[i]) { + /* Disable NULL ciphersuites: */ + case SSL_NULL_WITH_NULL_NULL: + case SSL_RSA_WITH_NULL_MD5: + case SSL_RSA_WITH_NULL_SHA: + case 0x003B: /* TLS_RSA_WITH_NULL_SHA256 */ + case SSL_FORTEZZA_DMS_WITH_NULL_SHA: + case 0xC001: /* TLS_ECDH_ECDSA_WITH_NULL_SHA */ + case 0xC006: /* TLS_ECDHE_ECDSA_WITH_NULL_SHA */ + case 0xC00B: /* TLS_ECDH_RSA_WITH_NULL_SHA */ + case 0xC010: /* TLS_ECDHE_RSA_WITH_NULL_SHA */ + case 0x002C: /* TLS_PSK_WITH_NULL_SHA */ + case 0x002D: /* TLS_DHE_PSK_WITH_NULL_SHA */ + case 0x002E: /* TLS_RSA_PSK_WITH_NULL_SHA */ + case 0x00B0: /* TLS_PSK_WITH_NULL_SHA256 */ + case 0x00B1: /* TLS_PSK_WITH_NULL_SHA384 */ + case 0x00B4: /* TLS_DHE_PSK_WITH_NULL_SHA256 */ + case 0x00B5: /* TLS_DHE_PSK_WITH_NULL_SHA384 */ + case 0x00B8: /* TLS_RSA_PSK_WITH_NULL_SHA256 */ + case 0x00B9: /* TLS_RSA_PSK_WITH_NULL_SHA384 */ + /* Disable anonymous ciphersuites: */ + case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: + case SSL_DH_anon_WITH_RC4_128_MD5: + case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DH_anon_WITH_DES_CBC_SHA: + case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + case 0xC015: /* TLS_ECDH_anon_WITH_NULL_SHA */ + case 0xC016: /* TLS_ECDH_anon_WITH_RC4_128_SHA */ + case 0xC017: /* TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA */ + case 0xC018: /* TLS_ECDH_anon_WITH_AES_128_CBC_SHA */ + case 0xC019: /* TLS_ECDH_anon_WITH_AES_256_CBC_SHA */ + case 0x006C: /* TLS_DH_anon_WITH_AES_128_CBC_SHA256 */ + case 0x006D: /* TLS_DH_anon_WITH_AES_256_CBC_SHA256 */ + case 0x00A6: /* TLS_DH_anon_WITH_AES_128_GCM_SHA256 */ + case 0x00A7: /* TLS_DH_anon_WITH_AES_256_GCM_SHA384 */ + /* Disable weak key ciphersuites: */ + case SSL_RSA_EXPORT_WITH_RC4_40_MD5: + case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: + case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: + case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: + case SSL_RSA_WITH_DES_CBC_SHA: + case SSL_DH_DSS_WITH_DES_CBC_SHA: + case SSL_DH_RSA_WITH_DES_CBC_SHA: + case SSL_DHE_DSS_WITH_DES_CBC_SHA: + case SSL_DHE_RSA_WITH_DES_CBC_SHA: + /* Disable IDEA: */ + case SSL_RSA_WITH_IDEA_CBC_SHA: + case SSL_RSA_WITH_IDEA_CBC_MD5: + /* Disable RC4: */ + case SSL_RSA_WITH_RC4_128_MD5: + case SSL_RSA_WITH_RC4_128_SHA: + case 0xC002: /* TLS_ECDH_ECDSA_WITH_RC4_128_SHA */ + case 0xC007: /* TLS_ECDHE_ECDSA_WITH_RC4_128_SHA*/ + case 0xC00C: /* TLS_ECDH_RSA_WITH_RC4_128_SHA */ + case 0xC011: /* TLS_ECDHE_RSA_WITH_RC4_128_SHA */ + case 0x008A: /* TLS_PSK_WITH_RC4_128_SHA */ + case 0x008E: /* TLS_DHE_PSK_WITH_RC4_128_SHA */ + case 0x0092: /* TLS_RSA_PSK_WITH_RC4_128_SHA */ + break; + default: /* enable everything else */ + allowed_ciphers[allowed_ciphers_count++] = all_ciphers[i]; + break; + } + } + err = SSLSetEnabledCiphers(BACKEND->ssl_ctx, allowed_ciphers, + allowed_ciphers_count); Curl_safefree(all_ciphers); Curl_safefree(allowed_ciphers); + if(err != noErr) { + failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); + return CURLE_SSL_CIPHER; + } #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 /* We want to enable 1/n-1 when using a CBC cipher unless the user @@ -2039,7 +2055,7 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen) if(len + n >= cap) { cap *= 2; - data = realloc(data, cap); + data = Curl_saferealloc(data, cap); if(!data) { close(fd); return -1; @@ -2057,35 +2073,6 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen) return 0; } -static int sslerr_to_curlerr(struct Curl_easy *data, int err) -{ - switch(err) { - case errSSLXCertChainInvalid: - failf(data, "SSL certificate problem: Invalid certificate chain"); - return CURLE_SSL_CACERT; - case errSSLUnknownRootCert: - failf(data, "SSL certificate problem: Untrusted root certificate"); - return CURLE_SSL_CACERT; - case errSSLNoRootCert: - failf(data, "SSL certificate problem: No root certificate"); - return CURLE_SSL_CACERT; - case errSSLCertExpired: - failf(data, "SSL certificate problem: Certificate chain had an " - "expired certificate"); - return CURLE_SSL_CACERT; - case errSSLBadCert: - failf(data, "SSL certificate problem: Couldn't understand the server " - "certificate format"); - return CURLE_SSL_CONNECT_ERROR; - case errSSLHostNameMismatch: - failf(data, "SSL certificate peer hostname mismatch"); - return CURLE_PEER_FAILED_VERIFICATION; - default: - failf(data, "SSL unexpected certificate error %d", err); - return CURLE_SSL_CACERT; - } -} - static int append_cert_to_array(struct Curl_easy *data, unsigned char *buf, size_t buflen, CFMutableArrayRef array) @@ -2103,13 +2090,20 @@ static int append_cert_to_array(struct Curl_easy *data, CFRelease(certdata); if(!cacert) { failf(data, "SSL: failed to create SecCertificate from CA certificate"); - return CURLE_SSL_CACERT; + return CURLE_SSL_CACERT_BADFILE; } /* Check if cacert is valid. */ result = CopyCertSubject(data, cacert, &certp); - if(result) - return result; + switch(result) { + case CURLE_OK: + break; + case CURLE_PEER_FAILED_VERIFICATION: + return CURLE_SSL_CACERT_BADFILE; + case CURLE_OUT_OF_MEMORY: + default: + return result; + } free(certp); CFArrayAppendValue(array, cacert); @@ -2128,7 +2122,7 @@ static int verify_cert(const char *cafile, struct Curl_easy *data, if(read_cert(cafile, &certbuf, &buflen) < 0) { failf(data, "SSL: failed to read or invalid CA certificate"); - return CURLE_SSL_CACERT; + return CURLE_SSL_CACERT_BADFILE; } /* @@ -2161,7 +2155,7 @@ static int verify_cert(const char *cafile, struct Curl_easy *data, CFRelease(array); failf(data, "SSL: invalid CA certificate #%d (offset %d) in bundle", n, offset); - return CURLE_SSL_CACERT; + return CURLE_SSL_CACERT_BADFILE; } offset += res; @@ -2195,22 +2189,27 @@ static int verify_cert(const char *cafile, struct Curl_easy *data, if(trust == NULL) { failf(data, "SSL: error getting certificate chain"); CFRelease(array); - return CURLE_OUT_OF_MEMORY; + return CURLE_PEER_FAILED_VERIFICATION; } else if(ret != noErr) { CFRelease(array); - return sslerr_to_curlerr(data, ret); + failf(data, "SSLCopyPeerTrust() returned error %d", ret); + return CURLE_PEER_FAILED_VERIFICATION; } ret = SecTrustSetAnchorCertificates(trust, array); if(ret != noErr) { + CFRelease(array); CFRelease(trust); - return sslerr_to_curlerr(data, ret); + failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); + return CURLE_PEER_FAILED_VERIFICATION; } ret = SecTrustSetAnchorCertificatesOnly(trust, true); if(ret != noErr) { + CFRelease(array); CFRelease(trust); - return sslerr_to_curlerr(data, ret); + failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret); + return CURLE_PEER_FAILED_VERIFICATION; } SecTrustResultType trust_eval = 0; @@ -2218,7 +2217,8 @@ static int verify_cert(const char *cafile, struct Curl_easy *data, CFRelease(array); CFRelease(trust); if(ret != noErr) { - return sslerr_to_curlerr(data, ret); + failf(data, "SecTrustEvaluate() returned error %d", ret); + return CURLE_PEER_FAILED_VERIFICATION; } switch(trust_eval) { @@ -2379,6 +2379,53 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex) /* the documentation says we need to call SSLHandshake() again */ return darwinssl_connect_step2(conn, sockindex); + /* Problem with encrypt / decrypt */ + case errSSLPeerDecodeError: + failf(data, "Decode failed"); + break; + case errSSLDecryptionFail: + case errSSLPeerDecryptionFail: + failf(data, "Decryption failed"); + break; + case errSSLPeerDecryptError: + failf(data, "A decryption error occurred"); + break; + case errSSLBadCipherSuite: + failf(data, "A bad SSL cipher suite was encountered"); + break; + case errSSLCrypto: + failf(data, "An underlying cryptographic error was encountered"); + break; +#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 + case errSSLWeakPeerEphemeralDHKey: + failf(data, "Indicates a weak ephemeral Diffie-Hellman key"); + break; +#endif + + /* Problem with the message record validation */ + case errSSLBadRecordMac: + case errSSLPeerBadRecordMac: + failf(data, "A record with a bad message authentication code (MAC) " + "was encountered"); + break; + case errSSLRecordOverflow: + case errSSLPeerRecordOverflow: + failf(data, "A record overflow occurred"); + break; + + /* Problem with zlib decompression */ + case errSSLPeerDecompressFail: + failf(data, "Decompression failed"); + break; + + /* Problem with access */ + case errSSLPeerAccessDenied: + failf(data, "Access was denied"); + break; + case errSSLPeerInsufficientSecurity: + failf(data, "There is insufficient security for this operation"); + break; + /* These are all certificate problems with the server: */ case errSSLXCertChainInvalid: failf(data, "SSL certificate problem: Invalid certificate chain"); @@ -2389,28 +2436,44 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex) case errSSLNoRootCert: failf(data, "SSL certificate problem: No root certificate"); return CURLE_SSL_CACERT; + case errSSLCertNotYetValid: + failf(data, "SSL certificate problem: The certificate chain had a " + "certificate that is not yet valid"); + return CURLE_SSL_CACERT; case errSSLCertExpired: + case errSSLPeerCertExpired: failf(data, "SSL certificate problem: Certificate chain had an " "expired certificate"); return CURLE_SSL_CACERT; case errSSLBadCert: + case errSSLPeerBadCert: failf(data, "SSL certificate problem: Couldn't understand the server " "certificate format"); - return CURLE_SSL_CONNECT_ERROR; + return CURLE_SSL_CACERT; + case errSSLPeerUnsupportedCert: + failf(data, "SSL certificate problem: An unsupported certificate " + "format was encountered"); + return CURLE_SSL_CACERT; + case errSSLPeerCertRevoked: + failf(data, "SSL certificate problem: The certificate was revoked"); + return CURLE_SSL_CACERT; + case errSSLPeerCertUnknown: + failf(data, "SSL certificate problem: The certificate is unknown"); + return CURLE_SSL_CACERT; /* These are all certificate problems with the client: */ case errSecAuthFailed: failf(data, "SSL authentication failed"); - return CURLE_SSL_CONNECT_ERROR; + break; case errSSLPeerHandshakeFail: failf(data, "SSL peer handshake failed, the server most likely " "requires a client certificate to connect"); - return CURLE_SSL_CONNECT_ERROR; + break; case errSSLPeerUnknownCA: failf(data, "SSL server rejected the client certificate due to " "the certificate being signed by an unknown certificate " "authority"); - return CURLE_SSL_CONNECT_ERROR; + break; /* This error is raised if the server's cert didn't match the server's host name: */ @@ -2419,30 +2482,98 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex) "certificate did not match \"%s\"\n", conn->host.dispname); return CURLE_PEER_FAILED_VERIFICATION; + /* Problem with SSL / TLS negotiation */ + case errSSLNegotiation: + failf(data, "Could not negotiate an SSL cipher suite with the server"); + break; + case errSSLBadConfiguration: + failf(data, "A configuration error occurred"); + break; + case errSSLProtocol: + failf(data, "SSL protocol error"); + break; + case errSSLPeerProtocolVersion: + failf(data, "A bad protocol version was encountered"); + break; + case errSSLPeerNoRenegotiation: + failf(data, "No renegotiation is allowed"); + break; + /* Generic handshake errors: */ case errSSLConnectionRefused: failf(data, "Server dropped the connection during the SSL handshake"); - return CURLE_SSL_CONNECT_ERROR; + break; case errSSLClosedAbort: failf(data, "Server aborted the SSL handshake"); - return CURLE_SSL_CONNECT_ERROR; - case errSSLNegotiation: - failf(data, "Could not negotiate an SSL cipher suite with the server"); - return CURLE_SSL_CONNECT_ERROR; + break; + case errSSLClosedGraceful: + failf(data, "The connection closed gracefully"); + break; + case errSSLClosedNoNotify: + failf(data, "The server closed the session with no notification"); + break; /* Sometimes paramErr happens with buggy ciphers: */ - case paramErr: case errSSLInternal: + case paramErr: + case errSSLInternal: + case errSSLPeerInternalError: failf(data, "Internal SSL engine error encountered during the " "SSL handshake"); - return CURLE_SSL_CONNECT_ERROR; + break; case errSSLFatalAlert: failf(data, "Fatal SSL engine error encountered during the SSL " "handshake"); - return CURLE_SSL_CONNECT_ERROR; + break; + /* Unclassified error */ + case errSSLBufferOverflow: + failf(data, "An insufficient buffer was provided"); + break; + case errSSLIllegalParam: + failf(data, "An illegal parameter was encountered"); + break; + case errSSLModuleAttach: + failf(data, "Module attach failure"); + break; + case errSSLSessionNotFound: + failf(data, "An attempt to restore an unknown session failed"); + break; + case errSSLPeerExportRestriction: + failf(data, "An export restriction occurred"); + break; + case errSSLPeerUserCancelled: + failf(data, "The user canceled the operation"); + break; + case errSSLPeerUnexpectedMsg: + failf(data, "Peer rejected unexpected message"); + break; +#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 + /* Treaing non-fatal error as fatal like before */ + case errSSLClientHelloReceived: + failf(data, "A non-fatal result for providing a server name " + "indication"); + break; +#endif + + /* Error codes defined in the enum but should never be returned. + We list them here just in case. */ +#if CURL_BUILD_MAC_10_6 + /* Only returned when kSSLSessionOptionBreakOnCertRequested is set */ + case errSSLClientCertRequested: + failf(data, "The server has requested a client certificate"); + break; +#endif +#if CURL_BUILD_MAC_10_9 + /* Alias for errSSLLast, end of error range */ + case errSSLUnexpectedRecord: + failf(data, "Unexpected (skipped) record in DTLS"); + break; +#endif default: + /* May also return codes listed in Security Framework Result Codes */ failf(data, "Unknown SSL protocol error in connection to %s:%d", hostname, err); - return CURLE_SSL_CONNECT_ERROR; + break; } + return CURLE_SSL_CONNECT_ERROR; } else { /* we have been connected fine, we're not waiting for anything else. */ diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c index a0b4960..8d1b3d6 100644 --- a/Utilities/cmcurl/lib/vtls/gskit.c +++ b/Utilities/cmcurl/lib/vtls/gskit.c @@ -766,8 +766,6 @@ set_ssl_version_min_max(unsigned int *protoflags, struct connectdata *conn) long i = ssl_version; switch(ssl_version_max) { case CURL_SSLVERSION_MAX_NONE: - ssl_version_max = ssl_version; - break; case CURL_SSLVERSION_MAX_DEFAULT: ssl_version_max = CURL_SSLVERSION_TLSv1_2; break; @@ -1316,8 +1314,7 @@ static int Curl_gskit_shutdown(struct connectdata *conn, int sockindex) static size_t Curl_gskit_version(char *buffer, size_t size) { - strncpy(buffer, "GSKit", size); - return strlen(buffer); + return snprintf(buffer, size, "GSKit"); } diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index 207b0fd..37662a7 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -94,6 +94,10 @@ static bool gtls_inited = FALSE; # endif #endif +#if (GNUTLS_VERSION_NUMBER >= 0x030603) +#define HAS_TLS13 +#endif + #ifdef HAS_OCSP # include <gnutls/ocsp.h> #endif @@ -390,9 +394,10 @@ set_ssl_version_min_max(int *list, size_t list_size, struct connectdata *conn) switch(ssl_version_max) { case CURL_SSLVERSION_MAX_NONE: - ssl_version_max = ssl_version << 16; - break; 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; } @@ -410,8 +415,13 @@ set_ssl_version_min_max(int *list, size_t list_size, struct connectdata *conn) 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; @@ -429,13 +439,9 @@ set_ssl_version_min_max(const char **prioritylist, 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); - if(ssl_version == CURL_SSLVERSION_TLSv1_3 || - ssl_version_max == CURL_SSLVERSION_MAX_TLSv1_3) { - failf(data, "GnuTLS: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; - } + if(ssl_version_max == CURL_SSLVERSION_MAX_NONE) { - ssl_version_max = ssl_version << 16; + ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT; } switch(ssl_version | ssl_version_max) { case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0: @@ -447,7 +453,6 @@ set_ssl_version_min_max(const char **prioritylist, struct connectdata *conn) "+VERS-TLS1.0:+VERS-TLS1.1:" GNUTLS_SRP; return CURLE_OK; case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2: - 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:" GNUTLS_SRP; return CURLE_OK; @@ -456,15 +461,54 @@ set_ssl_version_min_max(const char **prioritylist, struct connectdata *conn) "+VERS-TLS1.1:" GNUTLS_SRP; return CURLE_OK; case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_2: - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_DEFAULT: *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: - case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT: *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; } failf(data, "GnuTLS: cannot set ssl protocol"); @@ -677,6 +721,9 @@ gtls_connect_step1(struct connectdata *conn, 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: @@ -709,11 +756,14 @@ gtls_connect_step1(struct connectdata *conn, switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_SSLv3: prioritylist = GNUTLS_CIPHERS ":-VERS-TLS-ALL:+VERS-SSL3.0"; - sni = false; break; case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: - prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:" GNUTLS_SRP; + prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:" +#ifdef HAS_TLS13 + "+VERS-TLS1.3:" +#endif + GNUTLS_SRP; break; case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: @@ -1102,8 +1152,8 @@ gtls_connect_step3(struct connectdata *conn, return CURLE_SSL_INVALIDCERTSTATUS; } - rc = gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, - &status, NULL, NULL, NULL, &reason); + (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, + &status, NULL, NULL, NULL, &reason); switch(status) { case GNUTLS_OCSP_CERT_GOOD: @@ -1589,7 +1639,7 @@ static int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) ssize_t result; int retval = 0; struct Curl_easy *data = conn->data; - int done = 0; + bool done = FALSE; char buf[120]; /* This has only been tested on the proftpd server, and the mod_tls code @@ -1613,7 +1663,7 @@ static int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) case 0: /* This is the expected response. There was no data but only the close notify alert */ - done = 1; + done = TRUE; break; case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: @@ -1621,21 +1671,20 @@ static int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) break; default: retval = -1; - done = 1; + done = TRUE; break; } } else if(0 == what) { /* timeout */ failf(data, "SSL shutdown timeout"); - done = 1; - break; + done = TRUE; } else { /* anything that gets here is fatally bad */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); retval = -1; - done = 1; + done = TRUE; } } gnutls_deinit(BACKEND->session); diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index d7759dc..c5ed887 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -205,14 +205,11 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: ssl_version = CURL_SSLVERSION_TLSv1_0; - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; break; } switch(ssl_version_max) { case CURL_SSLVERSION_MAX_NONE: - ssl_version_max = ssl_version << 16; - break; case CURL_SSLVERSION_MAX_DEFAULT: ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; break; diff --git a/Utilities/cmcurl/lib/vtls/mesalink.c b/Utilities/cmcurl/lib/vtls/mesalink.c new file mode 100644 index 0000000..6a2b67e --- /dev/null +++ b/Utilities/cmcurl/lib/vtls/mesalink.c @@ -0,0 +1,627 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2017-2018, Yiming Jing, <jingyiming@baidu.com> + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.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 MesaLink-specific code for the TLS/SSL layer. No code + * but vtls.c should ever call or use these functions. + * + */ + +/* + * Based upon the CyaSSL implementation in cyassl.c and cyassl.h: + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * Thanks for code and inspiration! + */ + +#include "curl_setup.h" + +#ifdef USE_MESALINK + +#include <mesalink/options.h> +#include <mesalink/version.h> + +#include "urldata.h" +#include "sendf.h" +#include "inet_pton.h" +#include "vtls.h" +#include "parsedate.h" +#include "connect.h" /* for the connect timeout */ +#include "select.h" +#include "strcase.h" +#include "x509asn1.h" +#include "curl_printf.h" + +#include "mesalink.h" +#include <mesalink/openssl/ssl.h> +#include <mesalink/openssl/err.h> + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +#define MESALINK_MAX_ERROR_SZ 80 + +struct ssl_backend_data +{ + SSL_CTX *ctx; + SSL *handle; +}; + +#define BACKEND connssl->backend + +static Curl_recv mesalink_recv; +static Curl_send mesalink_send; + +/* + * This function loads all the client/CA certificates and CRLs. Setup the TLS + * layer and do all necessary magic. + */ +static CURLcode +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; +#endif + const char *const hostname = + SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; + size_t hostname_len = strlen(hostname); + + SSL_METHOD *req_method = NULL; + curl_socket_t sockfd = conn->sock[sockindex]; + + if(connssl->state == ssl_connection_complete) + return CURLE_OK; + + if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) { + failf(data, "MesaLink does not support to set maximum SSL/TLS version"); + return CURLE_SSL_CONNECT_ERROR; + } + + switch(SSL_CONN_CONFIG(version)) { + case CURL_SSLVERSION_SSLv3: + case CURL_SSLVERSION_TLSv1: + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + failf(data, "MesaLink does not support SSL 3.0, TLS 1.0, or TLS 1.1"); + return CURLE_NOT_BUILT_IN; + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1_2: + req_method = TLSv1_2_client_method(); + break; + case CURL_SSLVERSION_TLSv1_3: + req_method = TLSv1_3_client_method(); + break; + case CURL_SSLVERSION_SSLv2: + failf(data, "MesaLink does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(!req_method) { + failf(data, "SSL: couldn't create a method!"); + return CURLE_OUT_OF_MEMORY; + } + + if(BACKEND->ctx) + SSL_CTX_free(BACKEND->ctx); + BACKEND->ctx = SSL_CTX_new(req_method); + + if(!BACKEND->ctx) { + failf(data, "SSL: couldn't create a context!"); + return CURLE_OUT_OF_MEMORY; + } + + SSL_CTX_set_verify( + BACKEND->ctx, 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) { + failf(data, + "error setting certificate verify locations:\n" + " CAfile: %s\n CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + infof(data, + "error setting certificate verify locations," + " continuing anyway:\n"); + } + else { + infof(data, "successfully set certificate verify locations:\n"); + } + infof(data, + " CAfile: %s\n" + " CApath: %s\n", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + } + + ciphers = SSL_CONN_CONFIG(cipher_list); + if(ciphers) { +#ifdef MESALINK_HAVE_CIPHER + if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) { + failf(data, "failed setting cipher list: %s", ciphers); + return CURLE_SSL_CIPHER; + } +#endif + infof(data, "Cipher selection: %s\n", ciphers); + } + + 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; + } + + 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 + ) { + /* hostname is not a valid IP address */ + if(SSL_set_tlsext_host_name(BACKEND->handle, hostname) != SSL_SUCCESS) { + failf(data, + "WARNING: failed to configure server name indication (SNI) " + "TLS extension\n"); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { +#ifdef CURLDEBUG + /* Check if the hostname is 127.0.0.1 or [::1]; + * otherwise reject because MesaLink always wants a valid DNS Name + * specified in RFC 5280 Section 7.2 */ + if(strncmp(hostname, "127.0.0.1", 9) == 0 +#ifdef ENABLE_IPV6 + || strncmp(hostname, "[::1]", 5) == 0 +#endif + ) { + SSL_set_tlsext_host_name(BACKEND->handle, "localhost"); + } + else +#endif + { + failf(data, + "ERROR: MesaLink does not accept an IP address as a hostname\n"); + return CURLE_SSL_CONNECT_ERROR; + } + } + +#ifdef MESALINK_HAVE_SESSION + if(SSL_SET_OPTION(primary.sessionid)) { + void *ssl_sessionid = NULL; + + 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)) { + Curl_ssl_sessionid_unlock(conn); + failf( + data, + "SSL: SSL_set_session failed: %s", + ERR_error_string(SSL_get_error(BACKEND->handle, 0), error_buffer)); + return CURLE_SSL_CONNECT_ERROR; + } + /* Informational message */ + infof(data, "SSL re-using session ID\n"); + } + Curl_ssl_sessionid_unlock(conn); + } +#endif /* MESALINK_HAVE_SESSION */ + + if(SSL_set_fd(BACKEND->handle, (int)sockfd) != SSL_SUCCESS) { + failf(data, "SSL: SSL_set_fd failed"); + return CURLE_SSL_CONNECT_ERROR; + } + + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; +} + +static CURLcode +mesalink_connect_step2(struct connectdata *conn, int sockindex) +{ + int ret = -1; + struct Curl_easy *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + + conn->recv[sockindex] = mesalink_recv; + conn->send[sockindex] = mesalink_send; + + 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) { + connssl->connecting_state = ssl_connect_2_reading; + return CURLE_OK; + } + else { + failf(data, + "SSL_connect failed with error %d: %s", + detail, + ERR_error_string_n(detail, error_buffer, sizeof(error_buffer))); + ERR_print_errors_fp(stderr); + if(detail && SSL_CONN_CONFIG(verifypeer)) { + detail &= ~0xFF; + if(detail == TLS_ERROR_WEBPKI_ERRORS) { + failf(data, "Cert verify failed"); + return CURLE_PEER_FAILED_VERIFICATION; + } + } + return CURLE_SSL_CONNECT_ERROR; + } + } + + connssl->connecting_state = ssl_connect_3; + infof(data, + "SSL connection using %s / %s\n", + SSL_get_version(BACKEND->handle), + SSL_get_cipher_name(BACKEND->handle)); + + return CURLE_OK; +} + +static CURLcode +mesalink_connect_step3(struct connectdata *conn, int sockindex) +{ + CURLcode result = CURLE_OK; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + + DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + +#ifdef MESALINK_HAVE_SESSION + if(SSL_SET_OPTION(primary.sessionid)) { + bool incache; + SSL_SESSION *our_ssl_sessionid; + void *old_ssl_sessionid = NULL; + + our_ssl_sessionid = SSL_get_session(BACKEND->handle); + + Curl_ssl_sessionid_lock(conn); + incache = + !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL, sockindex)); + if(incache) { + if(old_ssl_sessionid != our_ssl_sessionid) { + infof(data, "old SSL session ID is stale, removing\n"); + Curl_ssl_delsessionid(conn, old_ssl_sessionid); + incache = FALSE; + } + } + + if(!incache) { + result = Curl_ssl_addsessionid( + conn, our_ssl_sessionid, 0 /* unknown size */, sockindex); + if(result) { + Curl_ssl_sessionid_unlock(conn); + failf(data, "failed to store ssl session"); + return result; + } + } + Curl_ssl_sessionid_unlock(conn); + } +#endif /* MESALINK_HAVE_SESSION */ + + connssl->connecting_state = ssl_connect_done; + + return result; +} + +static ssize_t +mesalink_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[MESALINK_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); + switch(err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* there's data pending, re-invoke SSL_write() */ + *curlcode = CURLE_AGAIN; + return -1; + default: + failf(conn->data, + "SSL write: %s, errno %d", + ERR_error_string_n(err, error_buffer, sizeof(error_buffer)), + SOCKERRNO); + *curlcode = CURLE_SEND_ERROR; + return -1; + } + } + return rc; +} + +static void +Curl_mesalink_close(struct connectdata *conn, int sockindex) +{ + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + + 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; + } +} + +static ssize_t +mesalink_recv(struct connectdata *conn, int num, char *buf, size_t buffersize, + CURLcode *curlcode) +{ + struct ssl_connect_data *connssl = &conn->ssl[num]; + char error_buffer[MESALINK_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); + + switch(err) { + case SSL_ERROR_ZERO_RETURN: /* no more data */ + case IO_ERROR_CONNECTION_ABORTED: + break; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* there's data pending, re-invoke SSL_read() */ + *curlcode = CURLE_AGAIN; + return -1; + default: + failf(conn->data, + "SSL read: %s, errno %d", + ERR_error_string_n(err, error_buffer, sizeof(error_buffer)), + SOCKERRNO); + *curlcode = CURLE_RECV_ERROR; + return -1; + } + } + return nread; +} + +static size_t +Curl_mesalink_version(char *buffer, size_t size) +{ + return snprintf(buffer, size, "MesaLink/%s", MESALINK_VERSION_STRING); +} + +static int +Curl_mesalink_init(void) +{ + return (SSL_library_init() == SSL_SUCCESS); +} + +/* + * This function is called to shut down the SSL layer but keep the + * socket open (CCC - Clear Command Channel) + */ +static int +Curl_mesalink_shutdown(struct connectdata *conn, int sockindex) +{ + int retval = 0; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + + if(BACKEND->handle) { + SSL_free(BACKEND->handle); + BACKEND->handle = NULL; + } + return retval; +} + +static CURLcode +mesalink_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]; + time_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) { + /* 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 = mesalink_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 = mesalink_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 = mesalink_connect_step3(conn, sockindex); + if(result) + return result; + } + + if(ssl_connect_done == connssl->connecting_state) { + connssl->state = ssl_connection_complete; + conn->recv[sockindex] = mesalink_recv; + conn->send[sockindex] = mesalink_send; + *done = TRUE; + } + else + *done = FALSE; + + /* Reset our connect state machine */ + connssl->connecting_state = ssl_connect_1; + + return CURLE_OK; +} + +static CURLcode +Curl_mesalink_connect_nonblocking(struct connectdata *conn, int sockindex, + bool *done) +{ + return mesalink_connect_common(conn, sockindex, TRUE, done); +} + +static CURLcode +Curl_mesalink_connect(struct connectdata *conn, int sockindex) +{ + CURLcode result; + bool done = FALSE; + + result = mesalink_connect_common(conn, sockindex, FALSE, &done); + if(result) + return result; + + DEBUGASSERT(done); + + return CURLE_OK; +} + +static void * +Curl_mesalink_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + (void)info; + return BACKEND->handle; +} + +const struct Curl_ssl Curl_ssl_mesalink = { + { CURLSSLBACKEND_MESALINK, "MesaLink" }, /* info */ + + SSLSUPP_SSL_CTX, + + sizeof(struct ssl_backend_data), + + Curl_mesalink_init, /* init */ + Curl_none_cleanup, /* cleanup */ + Curl_mesalink_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + Curl_mesalink_shutdown, /* shutdown */ + Curl_none_data_pending, /* data_pending */ + Curl_none_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + Curl_mesalink_connect, /* connect */ + Curl_mesalink_connect_nonblocking, /* connect_nonblocking */ + Curl_mesalink_get_internals, /* get_internals */ + Curl_mesalink_close, /* close_one */ + Curl_none_close_all, /* close_all */ + Curl_none_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 */ + NULL /* sha256sum */ +}; + +#endif diff --git a/Utilities/cmcurl/lib/vtls/mesalink.h b/Utilities/cmcurl/lib/vtls/mesalink.h new file mode 100644 index 0000000..54cb94a --- /dev/null +++ b/Utilities/cmcurl/lib/vtls/mesalink.h @@ -0,0 +1,32 @@ +#ifndef HEADER_CURL_MESALINK_H +#define HEADER_CURL_MESALINK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2017-2018, Yiming Jing, <jingyiming@baidu.com> + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.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_MESALINK + +extern const struct Curl_ssl Curl_ssl_mesalink; + +#endif /* USE_MESALINK */ +#endif /* HEADER_CURL_MESALINK_H */ diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c index 89f8183..a3d3e58 100644 --- a/Utilities/cmcurl/lib/vtls/nss.c +++ b/Utilities/cmcurl/lib/vtls/nss.c @@ -217,10 +217,15 @@ static const cipher_s cipherlist[] = { #endif }; +#ifdef WIN32 +static const char *pem_library = "nsspem.dll"; +static const char *trust_library = "nssckbi.dll"; +#else static const char *pem_library = "libnsspem.so"; -static SECMODModule *pem_module = NULL; - static const char *trust_library = "libnssckbi.so"; +#endif + +static SECMODModule *pem_module = NULL; static SECMODModule *trust_module = NULL; /* NSPR I/O layer we use to detect blocking direction during SSL handshake */ @@ -1522,7 +1527,6 @@ static bool is_nss_error(CURLcode err) { switch(err) { case CURLE_PEER_FAILED_VERIFICATION: - case CURLE_SSL_CACERT: case CURLE_SSL_CERTPROBLEM: case CURLE_SSL_CONNECT_ERROR: case CURLE_SSL_ISSUER_ERROR: @@ -1579,8 +1583,9 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn, infof(data, "%s %s\n", (result) ? "failed to load" : "loaded", trust_library); if(result == CURLE_FAILED_INIT) - /* make the error non-fatal if we are not going to verify peer */ - result = CURLE_SSL_CACERT_BADFILE; + /* If libnssckbi.so is not available (or fails to load), one can still + use CA certificates stored in NSS database. Ignore the failure. */ + result = CURLE_OK; } else if(!use_trust_module && trust_module) { /* libnssckbi.so not needed but already loaded --> unload it! */ @@ -1715,8 +1720,6 @@ static CURLcode nss_init_sslver(SSLVersionRange *sslver, failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); return result; } - if(max == CURL_SSLVERSION_MAX_NONE) - sslver->max = sslver->min; } switch(max) { diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index a487f55..4c5e8c1 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -69,7 +69,7 @@ #include <openssl/ocsp.h> #endif -#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) && /* 1.0.0 or later */ \ +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && /* 0.9.8 or later */ \ !defined(OPENSSL_NO_ENGINE) #define USE_OPENSSL_ENGINE #include <openssl/engine.h> @@ -129,16 +129,15 @@ #define X509_get0_notBefore(x) X509_get_notBefore(x) #define X509_get0_notAfter(x) X509_get_notAfter(x) #define CONST_EXTS /* nope */ -#ifdef LIBRESSL_VERSION_NUMBER -static unsigned long OpenSSL_version_num(void) -{ - return LIBRESSL_VERSION_NUMBER; -} -#else +#ifndef LIBRESSL_VERSION_NUMBER #define OpenSSL_version_num() SSLeay() #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) @@ -178,6 +177,7 @@ static unsigned long OpenSSL_version_num(void) !defined(LIBRESSL_VERSION_NUMBER) && \ !defined(OPENSSL_IS_BORINGSSL)) #define HAVE_SSL_CTX_SET_CIPHERSUITES +#define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH #endif #if defined(LIBRESSL_VERSION_NUMBER) @@ -253,7 +253,7 @@ static void ossl_keylog_callback(const SSL *ssl, const char *line) if(!buf) return; } - strncpy(buf, line, linelen); + memcpy(buf, line, linelen); buf[linelen] = '\n'; buf[linelen + 1] = '\0'; @@ -978,7 +978,7 @@ static int Curl_ossl_init(void) OPENSSL_load_builtin_modules(); -#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES +#ifdef USE_OPENSSL_ENGINE ENGINE_load_builtin_engines(); #endif @@ -994,9 +994,11 @@ static int Curl_ossl_init(void) #define CONF_MFLAGS_DEFAULT_SECTION 0x0 #endif +#ifndef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG CONF_modules_load_file(NULL, NULL, CONF_MFLAGS_DEFAULT_SECTION| CONF_MFLAGS_IGNORE_MISSING_FILE); +#endif #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ !defined(LIBRESSL_VERSION_NUMBER) @@ -1260,7 +1262,7 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) ssize_t nread; int buffsize; int err; - int done = 0; + bool done = FALSE; /* This has only been tested on the proftpd server, and the mod_tls code sends a close notify alert without waiting for a close notify alert in @@ -1288,7 +1290,7 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) case SSL_ERROR_ZERO_RETURN: /* no more data */ /* This is the expected response. There was no data but only the close notify alert */ - done = 1; + done = TRUE; break; case SSL_ERROR_WANT_READ: /* there's data pending, re-invoke SSL_read() */ @@ -1297,7 +1299,7 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) case SSL_ERROR_WANT_WRITE: /* SSL wants a write. Really odd. Let's bail out. */ infof(data, "SSL_ERROR_WANT_WRITE\n"); - done = 1; + done = TRUE; break; default: /* openssl/ssl.h says "look at error stack/return value/errno" */ @@ -1307,20 +1309,20 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) ossl_strerror(sslerror, buf, sizeof(buf)) : SSL_ERROR_to_str(err)), SOCKERRNO); - done = 1; + done = TRUE; break; } } else if(0 == what) { /* timeout */ failf(data, "SSL shutdown timeout"); - done = 1; + done = TRUE; } else { /* anything that gets here is fatally bad */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); retval = -1; - done = 1; + done = TRUE; } } /* while()-loop for the select() */ @@ -1416,6 +1418,10 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, } #else { +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)dispname; + (void)data; +#endif if(Curl_cert_hostcheck(match_pattern, hostname)) { infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"\n", dispname, match_pattern); @@ -2080,6 +2086,7 @@ select_next_proto_cb(SSL *ssl, } #endif /* HAS_NPN */ +#ifndef CURL_DISABLE_VERBOSE_STRINGS static const char * get_ssl_version_txt(SSL *ssl) { @@ -2106,6 +2113,7 @@ get_ssl_version_txt(SSL *ssl) } return "unknown"; } +#endif static CURLcode set_ssl_version_min_max(long *ctx_options, struct connectdata *conn, @@ -2171,7 +2179,6 @@ set_ssl_version_min_max(long *ctx_options, struct connectdata *conn, #endif break; case CURL_SSLVERSION_MAX_TLSv1_3: - case CURL_SSLVERSION_MAX_DEFAULT: #ifdef TLS1_3_VERSION break; #else @@ -2459,7 +2466,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)) { - failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers); + failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13); return CURLE_SSL_CIPHER; } infof(data, "TLS 1.3 cipher selection: %s\n", ciphers13); @@ -2467,6 +2474,11 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) } #endif +#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); +#endif + #ifdef USE_TLS_SRP if(ssl_authtype == CURL_TLSAUTH_SRP) { char * const ssl_username = SSL_SET_OPTION(username); @@ -2521,7 +2533,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) } #ifdef CURL_CA_FALLBACK else if(verifypeer) { - /* verfying the peer without any CA certificates won't + /* 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); } @@ -3187,7 +3199,7 @@ static CURLcode servercert(struct connectdata *conn, { CURLcode result = CURLE_OK; int rc; - long lerr, len; + long lerr; struct Curl_easy *data = conn->data; X509 *issuer; BIO *fp = NULL; @@ -3210,7 +3222,7 @@ static CURLcode servercert(struct connectdata *conn, ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer)) ); BIO_free(mem); - return 0; + return CURLE_OUT_OF_MEMORY; } BACKEND->server_cert = SSL_get_peer_certificate(BACKEND->handle); @@ -3230,15 +3242,20 @@ static CURLcode servercert(struct connectdata *conn, buffer, sizeof(buffer)); infof(data, " subject: %s\n", rc?"[NONE]":buffer); - 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); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + { + long len; + 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)); - len = BIO_get_mem_data(mem, (char **) &ptr); - infof(data, " expire date: %.*s\n", len, ptr); - (void)BIO_reset(mem); + 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); + } +#endif BIO_free(mem); @@ -3257,7 +3274,7 @@ static CURLcode servercert(struct connectdata *conn, if(rc) { if(strict) failf(data, "SSL: couldn't get X509-issuer name!"); - result = CURLE_SSL_CONNECT_ERROR; + result = CURLE_PEER_FAILED_VERIFICATION; } else { infof(data, " issuer: %s\n", buffer); diff --git a/Utilities/cmcurl/lib/vtls/polarssl.c b/Utilities/cmcurl/lib/vtls/polarssl.c index 604cb4c..27af0cc 100644 --- a/Utilities/cmcurl/lib/vtls/polarssl.c +++ b/Utilities/cmcurl/lib/vtls/polarssl.c @@ -185,14 +185,11 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: ssl_version = CURL_SSLVERSION_TLSv1_0; - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; break; } switch(ssl_version_max) { case CURL_SSLVERSION_MAX_NONE: - ssl_version_max = ssl_version << 16; - break; case CURL_SSLVERSION_MAX_DEFAULT: ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; break; diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 8f6c301..e442692 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -180,8 +180,6 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn) switch(ssl_version_max) { case CURL_SSLVERSION_MAX_NONE: - ssl_version_max = ssl_version << 16; - break; case CURL_SSLVERSION_MAX_DEFAULT: ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; break; @@ -363,7 +361,7 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, sep = _tcschr(path, TEXT('\\')); if(sep == NULL) - return CURLE_SSL_CONNECT_ERROR; + return CURLE_SSL_CERTPROBLEM; store_name_len = sep - path; @@ -387,19 +385,19 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; else - return CURLE_SSL_CONNECT_ERROR; + return CURLE_SSL_CERTPROBLEM; *store_path = sep + 1; sep = _tcschr(*store_path, TEXT('\\')); if(sep == NULL) - return CURLE_SSL_CONNECT_ERROR; + return CURLE_SSL_CERTPROBLEM; *sep = 0; *thumbprint = sep + 1; if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) - return CURLE_SSL_CONNECT_ERROR; + return CURLE_SSL_CERTPROBLEM; return CURLE_OK; } @@ -612,7 +610,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) "last error is %x", cert_store_name, cert_store_path, GetLastError()); Curl_unicodefree(cert_path); - return CURLE_SSL_CONNECT_ERROR; + return CURLE_SSL_CERTPROBLEM; } cert_thumbprint.pbData = cert_thumbprint_data; @@ -623,7 +621,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) cert_thumbprint_data, &cert_thumbprint.cbData, NULL, NULL)) { Curl_unicodefree(cert_path); - return CURLE_SSL_CONNECT_ERROR; + return CURLE_SSL_CERTPROBLEM; } client_certs[0] = CertFindCertificateInStore( @@ -636,6 +634,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) schannel_cred.cCreds = 1; schannel_cred.paCred = client_certs; } + else { + /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ + return CURLE_SSL_CERTPROBLEM; + } CertCloseStore(cert_store, 0); } @@ -672,14 +674,20 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) CertFreeCertificateContext(client_certs[0]); if(sspi_status != SEC_E_OK) { - if(sspi_status == SEC_E_WRONG_PRINCIPAL) - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(conn, sspi_status)); - else - failf(data, "schannel: AcquireCredentialsHandle failed: %s", - Curl_sspi_strerror(conn, sspi_status)); + failf(data, "schannel: AcquireCredentialsHandle failed: %s", + Curl_sspi_strerror(conn, sspi_status)); Curl_safefree(BACKEND->cred); - return CURLE_SSL_CONNECT_ERROR; + 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; + } } } @@ -782,14 +790,32 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) Curl_unicodefree(host_name); if(sspi_status != SEC_I_CONTINUE_NEEDED) { - if(sspi_status == SEC_E_WRONG_PRINCIPAL) - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(conn, sspi_status)); - else - failf(data, "schannel: initial InitializeSecurityContext failed: %s", - Curl_sspi_strerror(conn, sspi_status)); Curl_safefree(BACKEND->ctxt); - return CURLE_SSL_CONNECT_ERROR; + switch(sspi_status) { + case SEC_E_INSUFFICIENT_MEMORY: + failf(data, "schannel: initial InitializeSecurityContext failed: %s", + Curl_sspi_strerror(conn, sspi_status)); + return CURLE_OUT_OF_MEMORY; + case SEC_E_WRONG_PRINCIPAL: + failf(data, "schannel: SNI or certificate check failed: %s", + Curl_sspi_strerror(conn, sspi_status)); + 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(conn, sspi_status)); + return CURLE_SSL_CONNECT_ERROR; + } } infof(data, "schannel: sending initial handshake data: " @@ -1004,14 +1030,31 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) } } else { - if(sspi_status == SEC_E_WRONG_PRINCIPAL) - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(conn, sspi_status)); - else - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(conn, sspi_status)); - return sspi_status == SEC_E_UNTRUSTED_ROOT ? - CURLE_SSL_CACERT : CURLE_SSL_CONNECT_ERROR; + switch(sspi_status) { + case SEC_E_INSUFFICIENT_MEMORY: + failf(data, "schannel: next InitializeSecurityContext failed: %s", + Curl_sspi_strerror(conn, sspi_status)); + return CURLE_OUT_OF_MEMORY; + case SEC_E_WRONG_PRINCIPAL: + failf(data, "schannel: SNI or certificate check failed: %s", + Curl_sspi_strerror(conn, sspi_status)); + 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(conn, sspi_status)); + return CURLE_SSL_CONNECT_ERROR; + } } /* check if there was additional remaining encrypted data */ @@ -1192,7 +1235,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) if((sspi_status != SEC_E_OK) || (ccert_context == NULL)) { failf(data, "schannel: failed to retrieve remote cert context"); - return CURLE_SSL_CONNECT_ERROR; + return CURLE_PEER_FAILED_VERIFICATION; } result = Curl_ssl_init_certinfo(data, 1); diff --git a/Utilities/cmcurl/lib/vtls/schannel.h b/Utilities/cmcurl/lib/vtls/schannel.h index 51417af..e491bd4 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.h +++ b/Utilities/cmcurl/lib/vtls/schannel.h @@ -41,7 +41,7 @@ * typedef struct X509_name_st X509_NAME; * etc. * - * this wil cause all kinds of C-preprocessing paste errors in + * this will cause all kinds of C-preprocessing paste errors in * BoringSSL's <openssl/x509.h>: So just undefine those defines here * (and only here). */ diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index 5a7092a..2516f56 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c @@ -135,7 +135,7 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, failf(data, "schannel: CA file exceeds max size of %u bytes", MAX_CAFILE_SIZE); - result = CURLE_OUT_OF_MEMORY; + result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } @@ -244,7 +244,7 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, CertFreeCertificateContext(cert_context); if(!add_cert_result) { failf(data, - "schannel: failed to add certificate from CA file '%s'" + "schannel: failed to add certificate from CA file '%s' " "to certificate store: %s", ca_file, Curl_strerror(conn, GetLastError())); result = CURLE_SSL_CACERT_BADFILE; @@ -319,6 +319,10 @@ static CURLcode verify_host(struct Curl_easy *data, * embedded null bytes. This appears to be undocumented behavior. */ cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); + if(!cert_hostname_buff) { + result = CURLE_OUT_OF_MEMORY; + goto cleanup; + } actual_len = CertGetNameString(pCertContextServer, CERT_NAME_DNS_TYPE, name_flags, diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index b61c640..6af39fe 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -1190,6 +1190,8 @@ const struct Curl_ssl *Curl_ssl = &Curl_ssl_polarssl; #elif defined(USE_SCHANNEL) &Curl_ssl_schannel; +#elif defined(USE_MESALINK) + &Curl_ssl_mesalink; #else #error "Missing struct Curl_ssl for selected SSL backend" #endif @@ -1225,6 +1227,9 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_SCHANNEL) &Curl_ssl_schannel, #endif +#if defined(USE_MESALINK) + &Curl_ssl_mesalink, +#endif NULL }; diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index 40f9d74..5cd1160 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -108,6 +108,7 @@ CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen, #include "schannel.h" /* Schannel SSPI version */ #include "darwinssl.h" /* SecureTransport (Darwin) version */ #include "mbedtls.h" /* mbedTLS versions */ +#include "mesalink.h" /* MesaLink versions */ #ifndef MAX_PINNED_PUBKEY_SIZE #define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */ diff --git a/Utilities/cmcurl/lib/x509asn1.c b/Utilities/cmcurl/lib/x509asn1.c index 72a0b4a..a576fc7 100644 --- a/Utilities/cmcurl/lib/x509asn1.c +++ b/Utilities/cmcurl/lib/x509asn1.c @@ -103,6 +103,9 @@ 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, + const char *beg, const char *end) + WARN_UNUSED_RESULT; static const char *getASN1Element(curl_asn1Element *elem, const char *beg, const char *end) @@ -223,7 +226,7 @@ static const char *bit2str(const char *beg, const char *end) static const char *int2str(const char *beg, const char *end) { - long val = 0; + unsigned long val = 0; size_t n = end - beg; /* Convert an ASN.1 integer value into its string representation. @@ -243,7 +246,7 @@ static const char *int2str(const char *beg, const char *end) do val = (val << 8) | *(const unsigned char *) beg++; while(beg < end); - return curl_maprintf("%s%lx", (val < 0 || val >= 10)? "0x": "", val); + return curl_maprintf("%s%lx", val >= 10? "0x": "", val); } static ssize_t @@ -602,10 +605,17 @@ static ssize_t encodeDN(char *buf, size_t n, curl_asn1Element *dn) for(p1 = dn->beg; p1 < dn->end;) { p1 = getASN1Element(&rdn, p1, dn->end); + if(!p1) + return -1; for(p2 = rdn.beg; p2 < rdn.end;) { p2 = getASN1Element(&atv, p2, rdn.end); + if(!p2) + return -1; p3 = getASN1Element(&oid, atv.beg, atv.end); - getASN1Element(&value, p3, atv.end); + if(!p3) + return -1; + if(!getASN1Element(&value, p3, atv.end)) + return -1; str = ASN1tostr(&oid, 0); if(!str) return -1; @@ -697,10 +707,15 @@ int Curl_parseX509(curl_X509certificate *cert, /* Get tbsCertificate. */ beg = getASN1Element(&tbsCertificate, beg, end); + if(!beg) + return -1; /* Skip the signatureAlgorithm. */ beg = getASN1Element(&cert->signatureAlgorithm, beg, end); + if(!beg) + return -1; /* Get the signatureValue. */ - getASN1Element(&cert->signature, beg, end); + if(!getASN1Element(&cert->signature, beg, end)) + return -1; /* Parse TBSCertificate. */ beg = tbsCertificate.beg; @@ -710,28 +725,47 @@ int Curl_parseX509(curl_X509certificate *cert, cert->version.beg = &defaultVersion; cert->version.end = &defaultVersion + sizeof(defaultVersion); beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; if(elem.tag == 0) { - getASN1Element(&cert->version, elem.beg, elem.end); + if(!getASN1Element(&cert->version, elem.beg, elem.end)) + return -1; beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; } cert->serialNumber = elem; /* Get signature algorithm. */ beg = getASN1Element(&cert->signatureAlgorithm, beg, end); /* Get issuer. */ beg = getASN1Element(&cert->issuer, beg, end); + if(!beg) + return -1; /* Get notBefore and notAfter. */ beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end); - getASN1Element(&cert->notAfter, ccp, elem.end); + if(!ccp) + return -1; + if(!getASN1Element(&cert->notAfter, ccp, elem.end)) + return -1; /* Get subject. */ beg = getASN1Element(&cert->subject, beg, end); + if(!beg) + return -1; /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */ beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end); + if(!beg) + return -1; ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm, - cert->subjectPublicKeyInfo.beg, - cert->subjectPublicKeyInfo.end); - getASN1Element(&cert->subjectPublicKey, ccp, - cert->subjectPublicKeyInfo.end); + cert->subjectPublicKeyInfo.beg, + cert->subjectPublicKeyInfo.end); + if(!ccp) + return -1; + if(!getASN1Element(&cert->subjectPublicKey, ccp, + cert->subjectPublicKeyInfo.end)) + return -1; /* Get optional issuerUiqueID, subjectUniqueID and extensions. */ cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0; cert->extensions.tag = elem.tag = 0; @@ -740,20 +774,30 @@ int Curl_parseX509(curl_X509certificate *cert, cert->subjectUniqueID.beg = cert->subjectUniqueID.end = ""; cert->extensions.header = NULL; cert->extensions.beg = cert->extensions.end = ""; - if(beg < end) + if(beg < end) { beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } if(elem.tag == 1) { cert->issuerUniqueID = elem; - if(beg < end) + if(beg < end) { beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } } if(elem.tag == 2) { cert->subjectUniqueID = elem; - if(beg < end) + if(beg < end) { beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } } if(elem.tag == 3) - getASN1Element(&cert->extensions, elem.beg, elem.end); + if(!getASN1Element(&cert->extensions, elem.beg, elem.end)) + return -1; return 0; } @@ -782,11 +826,14 @@ static const char *dumpAlgo(curl_asn1Element *param, /* Get algorithm parameters and return algorithm name. */ beg = getASN1Element(&oid, beg, end); + if(!beg) + return NULL; param->header = NULL; param->tag = 0; param->beg = param->end = end; if(beg < end) - getASN1Element(param, beg, end); + if(!getASN1Element(param, beg, end)) + return NULL; return OID2str(oid.beg, oid.end, TRUE); } @@ -821,10 +868,14 @@ static void do_pubkey(struct Curl_easy *data, int certnum, /* Generate all information records for the public key. */ /* Get the public key (single element). */ - getASN1Element(&pk, pubkey->beg + 1, pubkey->end); + if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end)) + return; if(strcasecompare(algo, "rsaEncryption")) { p = getASN1Element(&elem, pk.beg, pk.end); + if(!p) + return; + /* Compute key length. */ for(q = elem.beg; !*q && q < elem.end; q++) ; @@ -845,30 +896,34 @@ static void do_pubkey(struct Curl_easy *data, int certnum, } /* Generate coefficients. */ do_pubkey_field(data, certnum, "rsa(n)", &elem); - getASN1Element(&elem, p, pk.end); + if(!getASN1Element(&elem, p, pk.end)) + return; do_pubkey_field(data, certnum, "rsa(e)", &elem); } else if(strcasecompare(algo, "dsa")) { p = getASN1Element(&elem, param->beg, param->end); - do_pubkey_field(data, certnum, "dsa(p)", &elem); - p = getASN1Element(&elem, p, param->end); - do_pubkey_field(data, certnum, "dsa(q)", &elem); - getASN1Element(&elem, p, param->end); - do_pubkey_field(data, certnum, "dsa(g)", &elem); - do_pubkey_field(data, certnum, "dsa(pub_key)", &pk); + if(p) { + do_pubkey_field(data, certnum, "dsa(p)", &elem); + p = getASN1Element(&elem, p, param->end); + if(p) { + do_pubkey_field(data, certnum, "dsa(q)", &elem); + if(getASN1Element(&elem, p, param->end)) { + do_pubkey_field(data, certnum, "dsa(g)", &elem); + do_pubkey_field(data, certnum, "dsa(pub_key)", &pk); + } + } + } } else if(strcasecompare(algo, "dhpublicnumber")) { p = getASN1Element(&elem, param->beg, param->end); - do_pubkey_field(data, certnum, "dh(p)", &elem); - getASN1Element(&elem, param->beg, param->end); - do_pubkey_field(data, certnum, "dh(g)", &elem); - do_pubkey_field(data, certnum, "dh(pub_key)", &pk); - } -#if 0 /* Patent-encumbered. */ - else if(strcasecompare(algo, "ecPublicKey")) { - /* Left TODO. */ + if(p) { + do_pubkey_field(data, certnum, "dh(p)", &elem); + if(getASN1Element(&elem, param->beg, param->end)) { + do_pubkey_field(data, certnum, "dh(g)", &elem); + do_pubkey_field(data, certnum, "dh(pub_key)", &pk); + } + } } -#endif } CURLcode Curl_extract_certinfo(struct connectdata *conn, @@ -896,7 +951,7 @@ CURLcode Curl_extract_certinfo(struct connectdata *conn, /* Extract the certificate ASN.1 elements. */ if(Curl_parseX509(&cert, beg, end)) - return CURLE_OUT_OF_MEMORY; + return CURLE_PEER_FAILED_VERIFICATION; /* Subject. */ ccp = DNtostr(&cert.subject); @@ -1107,18 +1162,29 @@ CURLcode Curl_verifyhost(struct connectdata *conn, /* Process extensions. */ for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) { p = getASN1Element(&ext, p, cert.extensions.end); + if(!p) + return CURLE_PEER_FAILED_VERIFICATION; + /* Check if extension is a subjectAlternativeName. */ ext.beg = checkOID(ext.beg, ext.end, sanOID); if(ext.beg) { ext.beg = getASN1Element(&elem, ext.beg, ext.end); + if(!ext.beg) + return CURLE_PEER_FAILED_VERIFICATION; /* Skip critical if present. */ - if(elem.tag == CURL_ASN1_BOOLEAN) + if(elem.tag == CURL_ASN1_BOOLEAN) { ext.beg = getASN1Element(&elem, ext.beg, ext.end); + if(!ext.beg) + return CURLE_PEER_FAILED_VERIFICATION; + } /* Parse the octet string contents: is a single sequence. */ - getASN1Element(&elem, elem.beg, elem.end); + if(!getASN1Element(&elem, elem.beg, elem.end)) + return CURLE_PEER_FAILED_VERIFICATION; /* Check all GeneralNames. */ for(q = elem.beg; matched != 1 && q < elem.end;) { q = getASN1Element(&name, q, elem.end); + if(!q) + break; switch(name.tag) { case 2: /* DNS name. */ len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING, @@ -1131,8 +1197,8 @@ CURLcode Curl_verifyhost(struct connectdata *conn, break; case 7: /* IP address. */ - matched = (size_t) (name.end - q) == addrlen && - !memcmp(&addr, q, addrlen); + matched = (size_t) (name.end - name.beg) == addrlen && + !memcmp(&addr, name.beg, addrlen); break; } } @@ -1159,8 +1225,12 @@ CURLcode Curl_verifyhost(struct connectdata *conn, distinguished one to get the most significant one. */ while(q < cert.subject.end) { q = getASN1Element(&dn, q, cert.subject.end); + if(!q) + break; for(p = dn.beg; p < dn.end;) { p = getASN1Element(&elem, p, dn.end); + if(!p) + return CURLE_PEER_FAILED_VERIFICATION; /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */ elem.beg = checkOID(elem.beg, elem.end, cnOID); if(elem.beg) |